mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Merge tag 'version_1.33.2' into fix_rpi_less_than
version 1.33.2 - alpha
This commit is contained in:
		
						commit
						cbebff495b
					
				
					 90 changed files with 4977 additions and 2964 deletions
				
			
		|  | @ -18,6 +18,8 @@ sub debugf { | |||
|     printf @_ if $debug; | ||||
| } | ||||
| 
 | ||||
| our $loglevel = 0; | ||||
| 
 | ||||
| # load threads before Moo as required by it | ||||
| our $have_threads; | ||||
| BEGIN { | ||||
|  | @ -104,6 +106,10 @@ my $pause_sema = Thread::Semaphore->new; | |||
| my $parallel_sema; | ||||
| my $paused = 0; | ||||
| 
 | ||||
| # Set the logging level at the Slic3r XS module. | ||||
| $Slic3r::loglevel = (defined($ENV{'SLIC3R_LOGLEVEL'}) && $ENV{'SLIC3R_LOGLEVEL'} =~ /^[1-9]/) ? $ENV{'SLIC3R_LOGLEVEL'} : 0; | ||||
| set_logging_level($Slic3r::loglevel); | ||||
| 
 | ||||
| sub spawn_thread { | ||||
|     my ($cb) = @_; | ||||
|      | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ use Slic3r::GUI::ConfigWizard; | |||
| use Slic3r::GUI::Controller; | ||||
| use Slic3r::GUI::Controller::ManualControlDialog; | ||||
| use Slic3r::GUI::Controller::PrinterPanel; | ||||
| use Slic3r::GUI::GLShader; | ||||
| use Slic3r::GUI::MainFrame; | ||||
| use Slic3r::GUI::Notifier; | ||||
| use Slic3r::GUI::Plater; | ||||
|  |  | |||
|  | @ -19,7 +19,8 @@ package Slic3r::GUI::3DScene::Base; | |||
| use strict; | ||||
| use warnings; | ||||
| 
 | ||||
| use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS); | ||||
| use Wx qw(:timer); | ||||
| use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_TIMER); | ||||
| # must load OpenGL *before* Wx::GLCanvas | ||||
| use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); | ||||
| use base qw(Wx::GLCanvas Class::Accessor); | ||||
|  | @ -53,10 +54,15 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init | |||
|                               origin | ||||
|                               _mouse_pos | ||||
|                               _hover_volume_idx | ||||
| 
 | ||||
|                               _drag_volume_idx | ||||
|                               _drag_start_pos | ||||
|                               _drag_start_xy | ||||
|                               _dragged | ||||
| 
 | ||||
|                               layer_editing_enabled | ||||
|                               _layer_height_edited | ||||
| 
 | ||||
|                               _camera_type | ||||
|                               _camera_target | ||||
|                               _camera_distance | ||||
|  | @ -82,7 +88,10 @@ use constant VIEW_BOTTOM     => [0.0,180.0]; | |||
| use constant VIEW_FRONT      => [0.0,90.0]; | ||||
| use constant VIEW_REAR       => [180.0,90.0]; | ||||
| 
 | ||||
| #use constant GIMBALL_LOCK_THETA_MAX => 150; | ||||
| use constant MANIPULATION_IDLE          => 0; | ||||
| use constant MANIPULATION_DRAGGING      => 1; | ||||
| use constant MANIPULATION_LAYER_HEIGHT  => 2; | ||||
| 
 | ||||
| use constant GIMBALL_LOCK_THETA_MAX => 170; | ||||
| 
 | ||||
| # make OpenGL::Array thread-safe | ||||
|  | @ -131,6 +140,15 @@ sub new { | |||
|     $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); | ||||
|     $self->_camera_distance(0.); | ||||
| 
 | ||||
|     # Size of a layer height texture, used by a shader to color map the object print layers. | ||||
|     $self->{layer_preview_z_texture_width} = 512; | ||||
|     $self->{layer_preview_z_texture_height} = 512; | ||||
|     $self->{layer_height_edit_band_width} = 2.; | ||||
|     $self->{layer_height_edit_strength} = 0.005; | ||||
|     $self->{layer_height_edit_last_object_id} = -1; | ||||
|     $self->{layer_height_edit_last_z} = 0.; | ||||
|     $self->{layer_height_edit_last_action} = 0; | ||||
|      | ||||
|     $self->reset_objects; | ||||
|      | ||||
|     EVT_PAINT($self, sub { | ||||
|  | @ -144,57 +162,110 @@ sub new { | |||
|         $self->Resize( $self->GetSizeWH ); | ||||
|         $self->Refresh; | ||||
|     }); | ||||
|     EVT_MOUSEWHEEL($self, sub { | ||||
|         my ($self, $e) = @_; | ||||
|          | ||||
|         # Calculate the zoom delta and apply it to the current zoom factor | ||||
|         my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta(); | ||||
|         $zoom = max(min($zoom, 4), -4); | ||||
|         $zoom /= 10; | ||||
|         $self->_zoom($self->_zoom / (1-$zoom)); | ||||
|          | ||||
|         # In order to zoom around the mouse point we need to translate | ||||
|         # the camera target | ||||
|         my $size = Slic3r::Pointf->new($self->GetSizeWH); | ||||
|         my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #- | ||||
|         $self->_camera_target->translate( | ||||
|             # ($pos - $size/2) represents the vector from the viewport center | ||||
|             # to the mouse point. By multiplying it by $zoom we get the new, | ||||
|             # transformed, length of such vector. | ||||
|             # Since we want that point to stay fixed, we move our camera target | ||||
|             # in the opposite direction by the delta of the length of such vector | ||||
|             # ($zoom - 1). We then scale everything by 1/$self->_zoom since  | ||||
|             # $self->_camera_target is expressed in terms of model units. | ||||
|             -($pos->x - $size->x/2) * ($zoom) / $self->_zoom, | ||||
|             -($pos->y - $size->y/2) * ($zoom) / $self->_zoom, | ||||
|             0, | ||||
|         ) if 0; | ||||
|         $self->on_viewport_changed->() if $self->on_viewport_changed; | ||||
|         $self->_dirty(1); | ||||
|         $self->Refresh; | ||||
|     }); | ||||
|     EVT_MOUSEWHEEL($self, \&mouse_wheel_event); | ||||
|     EVT_MOUSE_EVENTS($self, \&mouse_event); | ||||
|      | ||||
|     $self->{layer_height_edit_timer_id} = &Wx::NewId(); | ||||
|     $self->{layer_height_edit_timer} = Wx::Timer->new($self, $self->{layer_height_edit_timer_id}); | ||||
|     EVT_TIMER($self, $self->{layer_height_edit_timer_id}, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         return if ! $self->_layer_height_edited; | ||||
|         return if $self->{layer_height_edit_last_object_id} == -1; | ||||
|         $self->_variable_layer_thickness_action(undef, 1); | ||||
|     }); | ||||
|      | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub Destroy { | ||||
|     my ($self) = @_; | ||||
|     $self->{layer_height_edit_timer}->Stop; | ||||
|     $self->DestroyGL; | ||||
|     return $self->SUPER::Destroy; | ||||
| } | ||||
| 
 | ||||
| sub _first_selected_object_id { | ||||
|     my ($self) = @_; | ||||
|     for my $i (0..$#{$self->volumes}) { | ||||
|         if ($self->volumes->[$i]->selected) { | ||||
|             return int($self->volumes->[$i]->select_group_id / 1000000); | ||||
|         } | ||||
|     } | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| # Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen. | ||||
| sub _variable_layer_thickness_bar_rect { | ||||
|     my ($self) = @_; | ||||
|     my ($cw, $ch) = $self->GetSizeWH; | ||||
|     my $bar_width = 70; | ||||
|     return ($cw - $bar_width, 0, $cw, $ch); | ||||
| } | ||||
| 
 | ||||
| sub _variable_layer_thickness_bar_rect_mouse_inside { | ||||
|    my ($self, $mouse_evt) = @_; | ||||
|    my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect; | ||||
|    return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom; | ||||
| } | ||||
| 
 | ||||
| sub _variable_layer_thickness_bar_mouse_cursor_z { | ||||
|    my ($self, $object_idx, $mouse_evt) = @_; | ||||
|    my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect; | ||||
|    return unscale($self->{print}->get_object($object_idx)->size->z) * ($bar_bottom - $mouse_evt->GetY - 1.) / ($bar_bottom - $bar_top); | ||||
| } | ||||
| 
 | ||||
| sub _variable_layer_thickness_action { | ||||
|     my ($self, $mouse_event, $do_modification) = @_; | ||||
|     # A volume is selected. Test, whether hovering over a layer thickness bar. | ||||
|     if (defined($mouse_event)) { | ||||
|         $self->{layer_height_edit_last_z} = $self->_variable_layer_thickness_bar_mouse_cursor_z($self->{layer_height_edit_last_object_id}, $mouse_event); | ||||
|         $self->{layer_height_edit_last_action} = $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1); | ||||
|     } | ||||
|     if ($self->{layer_height_edit_last_object_id} != -1) { | ||||
|         $self->{print}->get_object($self->{layer_height_edit_last_object_id})->adjust_layer_height_profile( | ||||
|             $self->{layer_height_edit_last_z}, | ||||
|             $self->{layer_height_edit_strength}, | ||||
|             $self->{layer_height_edit_band_width},  | ||||
|             $self->{layer_height_edit_last_action}); | ||||
|         $self->{print}->get_object($self->{layer_height_edit_last_object_id})->generate_layer_height_texture( | ||||
|             $self->volumes->[$self->{layer_height_edit_last_object_id}]->layer_height_texture_data->ptr, | ||||
|             $self->{layer_preview_z_texture_height}, | ||||
|             $self->{layer_preview_z_texture_width}); | ||||
|         $self->Refresh; | ||||
|         # Automatic action on mouse down with the same coordinate. | ||||
|         $self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub mouse_event { | ||||
|     my ($self, $e) = @_; | ||||
|      | ||||
|     my $pos = Slic3r::Pointf->new($e->GetPositionXY); | ||||
|     my $object_idx_selected = $self->{layer_height_edit_last_object_id} = ($self->layer_editing_enabled && $self->{print}) ? $self->_first_selected_object_id : -1; | ||||
| 
 | ||||
|     if ($e->Entering && &Wx::wxMSW) { | ||||
|         # wxMSW needs focus in order to catch mouse wheel events | ||||
|         $self->SetFocus; | ||||
|     } elsif ($e->LeftDClick) { | ||||
|         $self->on_double_click->() | ||||
|             if $self->on_double_click; | ||||
|         if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { | ||||
|         } elsif ($self->on_double_click) { | ||||
|             $self->on_double_click->(); | ||||
|         } | ||||
|     } elsif ($e->LeftDown || $e->RightDown) { | ||||
|         # If user pressed left or right button we first check whether this happened | ||||
|         # on a volume or not. | ||||
|         my $volume_idx = $self->_hover_volume_idx // -1; | ||||
|          | ||||
|         # select volume in this 3D canvas | ||||
|         if ($self->enable_picking) { | ||||
|         $self->_layer_height_edited(0); | ||||
|         if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { | ||||
|             # A volume is selected and the mouse is hovering over a layer thickness bar. | ||||
|             # Start editing the layer height. | ||||
|             $self->_layer_height_edited(1); | ||||
|             $self->_variable_layer_thickness_action($e, 1); | ||||
|         } else { | ||||
|             # Select volume in this 3D canvas. | ||||
|             # Don't deselect a volume if layer editing is enabled. We want the object to stay selected | ||||
|             # during the scene manipulation. | ||||
|             if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) { | ||||
|                 $self->deselect_volumes; | ||||
|                 $self->select_volume($volume_idx); | ||||
|                  | ||||
|  | @ -225,7 +296,8 @@ sub mouse_event { | |||
|                         if $self->on_right_click; | ||||
|                 } | ||||
|             } | ||||
|     } elsif ($e->Dragging && $e->LeftIsDown && defined($self->_drag_volume_idx)) { | ||||
|         } | ||||
|     } elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) { | ||||
|         # get new position at the same Z of the initial click point | ||||
|         my $mouse_ray = $self->mouse_ray($e->GetX, $e->GetY); | ||||
|         my $cur_pos = $mouse_ray->intersect_plane($self->_drag_start_pos->z); | ||||
|  | @ -250,7 +322,9 @@ sub mouse_event { | |||
|         $self->_dragged(1); | ||||
|         $self->Refresh; | ||||
|     } elsif ($e->Dragging) { | ||||
|         if ($e->LeftIsDown) { | ||||
|         if ($self->_layer_height_edited && $object_idx_selected != -1) { | ||||
|             $self->_variable_layer_thickness_action($e, 0); | ||||
|         } elsif ($e->LeftIsDown) { | ||||
|             # if dragging over blank area with left button, rotate | ||||
|             if (defined $self->_drag_start_pos) { | ||||
|                 my $orig = $self->_drag_start_pos; | ||||
|  | @ -305,14 +379,61 @@ sub mouse_event { | |||
|         $self->_drag_start_pos(undef); | ||||
|         $self->_drag_start_xy(undef); | ||||
|         $self->_dragged(undef); | ||||
|         $self->_layer_height_edited(undef); | ||||
|         $self->{layer_height_edit_timer}->Stop; | ||||
|     } elsif ($e->Moving) { | ||||
|         $self->_mouse_pos($pos); | ||||
|         $self->Refresh; | ||||
|         # Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor | ||||
|         # hovers over. | ||||
|         $self->Refresh if ($self->enable_picking); | ||||
|     } else { | ||||
|         $e->Skip(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub mouse_wheel_event { | ||||
|     my ($self, $e) = @_; | ||||
|      | ||||
|     if ($self->layer_editing_enabled && $self->{print}) { | ||||
|         my $object_idx_selected = $self->_first_selected_object_id; | ||||
|         if ($object_idx_selected != -1) { | ||||
|             # A volume is selected. Test, whether hovering over a layer thickness bar. | ||||
|             if ($self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { | ||||
|                 # Adjust the width of the selection. | ||||
|                 $self->{layer_height_edit_band_width} = max(min($self->{layer_height_edit_band_width} * (1 + 0.1 * $e->GetWheelRotation() / $e->GetWheelDelta()), 10.), 1.5); | ||||
|                 $self->Refresh; | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     # Calculate the zoom delta and apply it to the current zoom factor | ||||
|     my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta(); | ||||
|     $zoom = max(min($zoom, 4), -4); | ||||
|     $zoom /= 10; | ||||
|     $self->_zoom($self->_zoom / (1-$zoom)); | ||||
|      | ||||
|     # In order to zoom around the mouse point we need to translate | ||||
|     # the camera target | ||||
|     my $size = Slic3r::Pointf->new($self->GetSizeWH); | ||||
|     my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #- | ||||
|     $self->_camera_target->translate( | ||||
|         # ($pos - $size/2) represents the vector from the viewport center | ||||
|         # to the mouse point. By multiplying it by $zoom we get the new, | ||||
|         # transformed, length of such vector. | ||||
|         # Since we want that point to stay fixed, we move our camera target | ||||
|         # in the opposite direction by the delta of the length of such vector | ||||
|         # ($zoom - 1). We then scale everything by 1/$self->_zoom since  | ||||
|         # $self->_camera_target is expressed in terms of model units. | ||||
|         -($pos->x - $size->x/2) * ($zoom) / $self->_zoom, | ||||
|         -($pos->y - $size->y/2) * ($zoom) / $self->_zoom, | ||||
|         0, | ||||
|     ) if 0; | ||||
|     $self->on_viewport_changed->() if $self->on_viewport_changed; | ||||
|     $self->_dirty(1); | ||||
|     $self->Refresh; | ||||
| } | ||||
| 
 | ||||
| # Reset selection. | ||||
| sub reset_objects { | ||||
|     my ($self) = @_; | ||||
|  | @ -721,6 +842,21 @@ sub InitGL { | |||
|     return unless $self->GetContext; | ||||
|     $self->init(1); | ||||
| 
 | ||||
|     my $shader; | ||||
|     $shader = $self->{shader} = new Slic3r::GUI::GLShader | ||||
|         if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1); | ||||
|     if ($self->{shader}) { | ||||
|         my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader); | ||||
|         print $info if $info; | ||||
| 
 | ||||
|         ($self->{layer_preview_z_texture_id}) = glGenTextures_p(1); | ||||
|         glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); | ||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); | ||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); | ||||
|         glBindTexture(GL_TEXTURE_2D, 0); | ||||
|     } | ||||
| 
 | ||||
|     glClearColor(0, 0, 0, 1); | ||||
|     glColor3f(1, 0, 0); | ||||
|     glEnable(GL_DEPTH_TEST); | ||||
|  | @ -762,6 +898,13 @@ sub InitGL { | |||
|     glEnable(GL_MULTISAMPLE); | ||||
| } | ||||
| 
 | ||||
| sub DestroyGL { | ||||
|     my $self = shift; | ||||
|     if ($self->init && $self->GetContext) { | ||||
|         delete $self->{shader}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| sub Render { | ||||
|     my ($self, $dc) = @_; | ||||
|      | ||||
|  | @ -799,10 +942,14 @@ sub Render { | |||
|     glLightfv_p(GL_LIGHT0, GL_SPECULAR, 0.2, 0.2, 0.2, 1); | ||||
|     glLightfv_p(GL_LIGHT0, GL_DIFFUSE,  0.5, 0.5, 0.5, 1); | ||||
| 
 | ||||
|     # Head light | ||||
|     glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0); | ||||
|      | ||||
|     if ($self->enable_picking) { | ||||
|         # Render the object for picking. | ||||
|         # FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing. | ||||
|         # Better to use software ray-casting on a bounding-box hierarchy. | ||||
|         glDisable(GL_MULTISAMPLE); | ||||
|         glDisable(GL_LIGHTING); | ||||
|         $self->draw_volumes(1); | ||||
|         glFlush(); | ||||
|  | @ -829,6 +976,7 @@ sub Render { | |||
|         glFlush(); | ||||
|         glFinish(); | ||||
|         glEnable(GL_LIGHTING); | ||||
|         glEnable(GL_MULTISAMPLE); | ||||
|     } | ||||
|      | ||||
|     # draw fixed background | ||||
|  | @ -948,9 +1096,12 @@ sub Render { | |||
|         glDisable(GL_BLEND); | ||||
|     } | ||||
| 
 | ||||
|     glFlush(); | ||||
|     $self->draw_active_object_annotations; | ||||
|      | ||||
|     $self->SwapBuffers(); | ||||
| 
 | ||||
|     # Calling glFinish has a performance penalty, but it seems to fix some OpenGL driver hang-up with extremely large scenes. | ||||
|     glFinish(); | ||||
| } | ||||
| 
 | ||||
| sub draw_volumes { | ||||
|  | @ -963,10 +1114,61 @@ sub draw_volumes { | |||
|     glEnableClientState(GL_VERTEX_ARRAY); | ||||
|     glEnableClientState(GL_NORMAL_ARRAY); | ||||
|      | ||||
|     # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),  | ||||
|     # where x, y is the window size divided by $self->_zoom. | ||||
|     my ($cw, $ch) = $self->GetSizeWH; | ||||
|     my $bar_width = 70; | ||||
|     my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom)); | ||||
|     my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom)); | ||||
|     my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition()); | ||||
|     my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. : | ||||
|         ($ch - $mouse_pos->y - 1.) / ($ch - 1); | ||||
| 
 | ||||
|     foreach my $volume_idx (0..$#{$self->volumes}) { | ||||
|         my $volume = $self->volumes->[$volume_idx]; | ||||
| 
 | ||||
|         if ($fakecolor) { | ||||
|         my $shader_active = 0; | ||||
|         if ($self->layer_editing_enabled && ! $fakecolor && $volume->selected && $self->{shader} && $volume->{layer_height_texture_data} && $volume->{layer_height_texture_cells}) { | ||||
|             $self->{shader}->Enable; | ||||
|             my $z_to_texture_row_id             = $self->{shader}->Map('z_to_texture_row'); | ||||
|             my $z_texture_row_to_normalized_id  = $self->{shader}->Map('z_texture_row_to_normalized'); | ||||
|             my $z_cursor_id                     = $self->{shader}->Map('z_cursor'); | ||||
|             my $z_cursor_band_width_id          = $self->{shader}->Map('z_cursor_band_width'); | ||||
|             die if ! defined($z_to_texture_row_id); | ||||
|             die if ! defined($z_texture_row_to_normalized_id); | ||||
|             die if ! defined($z_cursor_id); | ||||
|             die if ! defined($z_cursor_band_width_id); | ||||
|             my $ncells = $volume->{layer_height_texture_cells}; | ||||
|             my $z_max = $volume->{bounding_box}->z_max; | ||||
|             glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max)); | ||||
|             glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height}); | ||||
|             glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative); | ||||
|             glUniform1fARB($z_cursor_band_width_id, $self->{layer_height_edit_band_width}); | ||||
|             glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); | ||||
| #            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LEVEL, 0); | ||||
| #            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); | ||||
|             if (1) { | ||||
|                 glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},  | ||||
|                     0, GL_RGBA, GL_UNSIGNED_BYTE, 0); | ||||
|                 glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, | ||||
|                     0, GL_RGBA, GL_UNSIGNED_BYTE, 0); | ||||
| #                glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | ||||
| #                glPixelStorei(GL_UNPACK_ROW_LENGTH, $self->{layer_preview_z_texture_width}); | ||||
|                 glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, | ||||
|                     GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr); | ||||
|                 glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, | ||||
|                     GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4)); | ||||
|             } else { | ||||
|                 glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},  | ||||
|                     0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr); | ||||
|                 glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width}/2, $self->{layer_preview_z_texture_height}/2,  | ||||
|                     0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr + $self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4); | ||||
|             } | ||||
| 
 | ||||
| #            my $nlines = ceil($ncells / ($self->{layer_preview_z_texture_width} - 1)); | ||||
| 
 | ||||
|             $shader_active = 1; | ||||
|         } elsif ($fakecolor) { | ||||
|             # Object picking mode. Render the object with a color encoding the object index. | ||||
|             my $r = ($volume_idx & 0x000000FF) >>  0; | ||||
|             my $g = ($volume_idx & 0x0000FF00) >>  8; | ||||
|  | @ -1017,16 +1219,39 @@ sub draw_volumes { | |||
|         if ($qverts_begin < $qverts_end) { | ||||
|             glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr); | ||||
|             glNormalPointer_c(GL_FLOAT, 0, $volume->qverts->norms_ptr); | ||||
|             glDrawArrays(GL_QUADS, $qverts_begin / 3, ($qverts_end-$qverts_begin) / 3); | ||||
|             $qverts_begin /= 3; | ||||
|             $qverts_end /= 3; | ||||
|             my $nvertices = $qverts_end-$qverts_begin; | ||||
|             while ($nvertices > 0) { | ||||
|                 my $nvertices_this = ($nvertices > 4096) ? 4096 : $nvertices; | ||||
|                 glDrawArrays(GL_QUADS, $qverts_begin, $nvertices_this); | ||||
|                 $qverts_begin += $nvertices_this; | ||||
|                 $nvertices -= $nvertices_this; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         if ($tverts_begin < $tverts_end) { | ||||
|             glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr); | ||||
|             glNormalPointer_c(GL_FLOAT, 0, $volume->tverts->norms_ptr); | ||||
|             glDrawArrays(GL_TRIANGLES, $tverts_begin / 3, ($tverts_end-$tverts_begin) / 3); | ||||
|             $tverts_begin /= 3; | ||||
|             $tverts_end /= 3; | ||||
|             my $nvertices = $tverts_end-$tverts_begin; | ||||
|             while ($nvertices > 0) { | ||||
|                 my $nvertices_this = ($nvertices > 4095) ? 4095 : $nvertices; | ||||
|                 glDrawArrays(GL_TRIANGLES, $tverts_begin, $nvertices_this); | ||||
|                 $tverts_begin += $nvertices_this; | ||||
|                 $nvertices -= $nvertices_this; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         glVertexPointer_c(3, GL_FLOAT, 0, 0); | ||||
|         glNormalPointer_c(GL_FLOAT, 0, 0); | ||||
|         glPopMatrix(); | ||||
| 
 | ||||
|         if ($shader_active) { | ||||
|             glBindTexture(GL_TEXTURE_2D, 0); | ||||
|             $self->{shader}->Disable; | ||||
|         } | ||||
|     } | ||||
|     glDisableClientState(GL_NORMAL_ARRAY); | ||||
|     glDisable(GL_BLEND); | ||||
|  | @ -1036,10 +1261,98 @@ sub draw_volumes { | |||
|         glColor3f(0, 0, 0); | ||||
|         glVertexPointer_p(3, $self->cut_lines_vertices); | ||||
|         glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3); | ||||
|         glVertexPointer_c(3, GL_FLOAT, 0, 0); | ||||
|     } | ||||
|     glDisableClientState(GL_VERTEX_ARRAY); | ||||
| } | ||||
| 
 | ||||
| sub draw_active_object_annotations { | ||||
|     # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. | ||||
|     my ($self) = @_; | ||||
| 
 | ||||
|     return if (! $self->{shader} || ! $self->layer_editing_enabled); | ||||
| 
 | ||||
|     my $volume; | ||||
|     foreach my $volume_idx (0..$#{$self->volumes}) { | ||||
|         my $v = $self->volumes->[$volume_idx]; | ||||
|         if ($v->selected && $v->{layer_height_texture_data} && $v->{layer_height_texture_cells}) { | ||||
|             $volume = $v; | ||||
|             last; | ||||
|         } | ||||
|     } | ||||
|     return if (! $volume); | ||||
|      | ||||
|     # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),  | ||||
|     # where x, y is the window size divided by $self->_zoom. | ||||
|     my ($cw, $ch) = $self->GetSizeWH; | ||||
|     my $bar_width = 70; | ||||
|     my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom)); | ||||
|     my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom)); | ||||
|     my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition()); | ||||
|     my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. : | ||||
|         ($ch - $mouse_pos->y - 1.) / ($ch - 1); | ||||
| 
 | ||||
|     $self->{shader}->Enable; | ||||
|     my $z_to_texture_row_id             = $self->{shader}->Map('z_to_texture_row'); | ||||
|     my $z_texture_row_to_normalized_id  = $self->{shader}->Map('z_texture_row_to_normalized'); | ||||
|     my $z_cursor_id                     = $self->{shader}->Map('z_cursor'); | ||||
|     my $ncells                          = $volume->{layer_height_texture_cells}; | ||||
|     my $z_max                           = $volume->{bounding_box}->z_max; | ||||
|     glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max)); | ||||
|     glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height}); | ||||
|     glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative); | ||||
|     glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id}); | ||||
|     glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},  | ||||
|         0, GL_RGBA, GL_UNSIGNED_BYTE, 0); | ||||
|     glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, | ||||
|         0, GL_RGBA, GL_UNSIGNED_BYTE, 0); | ||||
|     glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height}, | ||||
|         GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr); | ||||
|     glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2, | ||||
|         GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4)); | ||||
|      | ||||
|     # Render the color bar. | ||||
|     glDisable(GL_DEPTH_TEST); | ||||
|     # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),  | ||||
|     # where x, y is the window size divided by $self->_zoom. | ||||
|     glPushMatrix(); | ||||
|     glLoadIdentity(); | ||||
|     # Paint the overlay. | ||||
|     glBegin(GL_QUADS); | ||||
|     glVertex3f($bar_left,  $bar_bottom, 0); | ||||
|     glVertex3f($bar_right, $bar_bottom, 0); | ||||
|     glVertex3f($bar_right, $bar_top, $volume->{bounding_box}->z_max); | ||||
|     glVertex3f($bar_left,  $bar_top, $volume->{bounding_box}->z_max); | ||||
|     glEnd(); | ||||
|     glBindTexture(GL_TEXTURE_2D, 0); | ||||
|     $self->{shader}->Disable; | ||||
| 
 | ||||
|     # Paint the graph. | ||||
|     my $object_idx = int($volume->select_group_id / 1000000); | ||||
|     my $print_object = $self->{print}->get_object($object_idx); | ||||
|     my $max_z = unscale($print_object->size->z); | ||||
|     my $profile = $print_object->layer_height_profile; | ||||
|     my $layer_height = $print_object->config->get('layer_height'); | ||||
|     # Baseline | ||||
|     glColor3f(0., 0., 0.); | ||||
|     glBegin(GL_LINE_STRIP); | ||||
|     glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45,  $bar_bottom); | ||||
|     glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45,  $bar_top); | ||||
|     glEnd(); | ||||
|     # Curve | ||||
|     glColor3f(0., 0., 1.); | ||||
|     glBegin(GL_LINE_STRIP); | ||||
|     for (my $i = 0; $i < int(@{$profile}); $i += 2) { | ||||
|         my $z = $profile->[$i]; | ||||
|         my $h = $profile->[$i+1]; | ||||
|         glVertex3f($bar_left + $h * ($bar_right - $bar_left) / 0.45,  $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z); | ||||
|     } | ||||
|     glEnd(); | ||||
|     # Revert the matrices. | ||||
|     glPopMatrix(); | ||||
|     glEnable(GL_DEPTH_TEST); | ||||
| } | ||||
| 
 | ||||
| sub _report_opengl_state | ||||
| { | ||||
|     my ($self, $comment) = @_; | ||||
|  | @ -1069,6 +1382,112 @@ sub _report_opengl_state | |||
|     } | ||||
| } | ||||
| 
 | ||||
| sub _vertex_shader { | ||||
|     return <<'VERTEX'; | ||||
| #version 110 | ||||
| 
 | ||||
| #define LIGHT_TOP_DIR        0., 1., 0. | ||||
| #define LIGHT_TOP_DIFFUSE    0.2 | ||||
| #define LIGHT_TOP_SPECULAR   0.3 | ||||
| 
 | ||||
| #define LIGHT_FRONT_DIR      0., 0., 1. | ||||
| #define LIGHT_FRONT_DIFFUSE  0.5 | ||||
| #define LIGHT_FRONT_SPECULAR 0.3 | ||||
| 
 | ||||
| #define INTENSITY_AMBIENT    0.1 | ||||
| 
 | ||||
| uniform float z_to_texture_row; | ||||
| varying float intensity_specular; | ||||
| varying float intensity_tainted; | ||||
| varying float object_z; | ||||
| 
 | ||||
| void main() | ||||
| { | ||||
|     vec3 eye, normal, lightDir, viewVector, halfVector; | ||||
|     float NdotL, NdotHV; | ||||
| 
 | ||||
| //    eye = gl_ModelViewMatrixInverse[3].xyz; | ||||
|     eye = vec3(0., 0., 1.); | ||||
| 
 | ||||
|     // First transform the normal into eye space and normalize the result. | ||||
|     normal = normalize(gl_NormalMatrix * gl_Normal); | ||||
|      | ||||
|     // Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space.  | ||||
|     // Also since we're talking about a directional light, the position field is actually direction. | ||||
|     lightDir = vec3(LIGHT_TOP_DIR); | ||||
|     halfVector = normalize(lightDir + eye); | ||||
|      | ||||
|     // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. | ||||
|     // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. | ||||
|     NdotL = max(dot(normal, lightDir), 0.0); | ||||
| 
 | ||||
|     intensity_tainted = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; | ||||
|     intensity_specular = 0.; | ||||
| 
 | ||||
| //    if (NdotL > 0.0) | ||||
| //        intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), gl_FrontMaterial.shininess); | ||||
| 
 | ||||
|     // Perform the same lighting calculation for the 2nd light source. | ||||
|     lightDir = vec3(LIGHT_FRONT_DIR); | ||||
|     halfVector = normalize(lightDir + eye); | ||||
|     NdotL = max(dot(normal, lightDir), 0.0); | ||||
|     intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE; | ||||
|      | ||||
|     // compute the specular term if NdotL is larger than zero | ||||
|     if (NdotL > 0.0) | ||||
|         intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(normal, halfVector), 0.0), gl_FrontMaterial.shininess); | ||||
| 
 | ||||
|     // Scaled to widths of the Z texture. | ||||
|     object_z = gl_Vertex.z / gl_Vertex.w; | ||||
| 
 | ||||
|     gl_Position = ftransform(); | ||||
| }  | ||||
| 
 | ||||
| VERTEX | ||||
| } | ||||
| 
 | ||||
| sub _fragment_shader { | ||||
|     return <<'FRAGMENT'; | ||||
| #version 110 | ||||
| 
 | ||||
| #define M_PI 3.1415926535897932384626433832795 | ||||
| 
 | ||||
| // 2D texture (1D texture split by the rows) of color along the object Z axis. | ||||
| uniform sampler2D z_texture; | ||||
| // Scaling from the Z texture rows coordinate to the normalized texture row coordinate. | ||||
| uniform float z_to_texture_row; | ||||
| uniform float z_texture_row_to_normalized; | ||||
| 
 | ||||
| varying float intensity_specular; | ||||
| varying float intensity_tainted; | ||||
| varying float object_z; | ||||
| uniform float z_cursor; | ||||
| uniform float z_cursor_band_width; | ||||
| 
 | ||||
| void main() | ||||
| { | ||||
|     float object_z_row = z_to_texture_row * object_z; | ||||
|     // Index of the row in the texture. | ||||
|     float z_texture_row = floor(object_z_row); | ||||
|     // Normalized coordinate from 0. to 1. | ||||
|     float z_texture_col = object_z_row - z_texture_row; | ||||
| //    float z_blend = 0.5 + 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) / 3.))); | ||||
| //    float z_blend = 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor)))) + 0.5; | ||||
|     float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25; | ||||
|     // Scale z_texture_row to normalized coordinates. | ||||
|     // Sample the Z texture. | ||||
|     gl_FragColor =  | ||||
|         vec4(intensity_specular, intensity_specular, intensity_specular, 1.) +  | ||||
| //        intensity_tainted * texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5)), -2.5); | ||||
|         (1. - z_blend) * intensity_tainted * texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5)), -200.) +  | ||||
|         z_blend * vec4(1., 1., 0., 0.); | ||||
|     // and reset the transparency. | ||||
|     gl_FragColor.a = 1.; | ||||
| } | ||||
| 
 | ||||
| FRAGMENT | ||||
| } | ||||
| 
 | ||||
| # Container for object geometry and selection status. | ||||
| package Slic3r::GUI::3DScene::Volume; | ||||
| use Moo; | ||||
|  | @ -1076,6 +1495,8 @@ use Moo; | |||
| has 'bounding_box'      => (is => 'ro', required => 1); | ||||
| has 'origin'            => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); | ||||
| has 'color'             => (is => 'ro', required => 1); | ||||
| # An ID containing the object ID, volume ID and instance ID. | ||||
| has 'composite_id'      => (is => 'rw', default => sub { -1 }); | ||||
| # An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance. | ||||
| has 'select_group_id'   => (is => 'rw', default => sub { -1 }); | ||||
| # An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance. | ||||
|  | @ -1097,6 +1518,26 @@ has 'tverts'            => (is => 'rw'); | |||
| # The offsets stores tripples of (z_top, qverts_idx, tverts_idx) in a linear array. | ||||
| has 'offsets'           => (is => 'rw'); | ||||
| 
 | ||||
| # RGBA texture along the Z axis of an object, to visualize layers by stripes colored by their height. | ||||
| has 'layer_height_texture_data'   => (is => 'rw'); | ||||
| # Number of texture cells. | ||||
| has 'layer_height_texture_cells'  => (is => 'rw'); | ||||
| 
 | ||||
| sub object_idx { | ||||
|     my ($self) = @_; | ||||
|     return $self->composite_id / 1000000; | ||||
| } | ||||
| 
 | ||||
| sub volume_idx { | ||||
|     my ($self) = @_; | ||||
|     return ($self->composite_id / 1000) % 1000; | ||||
| } | ||||
| 
 | ||||
| sub instance_idx { | ||||
|     my ($self) = @_; | ||||
|     return $self->composite_id % 1000; | ||||
| } | ||||
| 
 | ||||
| sub transformed_bounding_box { | ||||
|     my ($self) = @_; | ||||
|      | ||||
|  | @ -1122,8 +1563,6 @@ __PACKAGE__->mk_accessors(qw( | |||
|     color_by | ||||
|     select_by | ||||
|     drag_by | ||||
|     volumes_by_object | ||||
|     _objects_by_volumes | ||||
| )); | ||||
| 
 | ||||
| sub new { | ||||
|  | @ -1133,14 +1572,12 @@ sub new { | |||
|     $self->color_by('volume');      # object | volume | ||||
|     $self->select_by('object');     # object | volume | instance | ||||
|     $self->drag_by('instance');     # object | instance | ||||
|     $self->volumes_by_object({});   # obj_idx => [ volume_idx, volume_idx ... ] | ||||
|     $self->_objects_by_volumes({}); # volume_idx => [ obj_idx, instance_idx ] | ||||
|      | ||||
|     return $self; | ||||
| } | ||||
| 
 | ||||
| sub load_object { | ||||
|     my ($self, $model, $obj_idx, $instance_idxs) = @_; | ||||
|     my ($self, $model, $print, $obj_idx, $instance_idxs) = @_; | ||||
|      | ||||
|     my $model_object; | ||||
|     if ($model->isa('Slic3r::Model::Object')) { | ||||
|  | @ -1153,6 +1590,19 @@ sub load_object { | |||
|      | ||||
|     $instance_idxs ||= [0..$#{$model_object->instances}]; | ||||
| 
 | ||||
|     # Object will have a single common layer height texture for all volumes. | ||||
|     my $layer_height_texture_data; | ||||
|     my $layer_height_texture_cells; | ||||
|     if ($print && $obj_idx < $print->object_count) { | ||||
|         # Generate the layer height texture. Allocate data for the 0th and 1st mipmap levels. | ||||
|         $layer_height_texture_data = OpenGL::Array->new($self->{layer_preview_z_texture_width}*$self->{layer_preview_z_texture_height}*5, GL_UNSIGNED_BYTE); | ||||
| #        $print->get_object($obj_idx)->update_layer_height_profile_from_ranges(); | ||||
|         $layer_height_texture_cells = $print->get_object($obj_idx)->generate_layer_height_texture( | ||||
|             $layer_height_texture_data->ptr, | ||||
|             $self->{layer_preview_z_texture_height}, | ||||
|             $self->{layer_preview_z_texture_width}); | ||||
|     } | ||||
|      | ||||
|     my @volumes_idx = (); | ||||
|     foreach my $volume_idx (0..$#{$model_object->volumes}) { | ||||
|         my $volume = $model_object->volumes->[$volume_idx]; | ||||
|  | @ -1174,16 +1624,18 @@ sub load_object { | |||
|             # not correspond to the color of the filament. | ||||
|             my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; | ||||
|             $color->[3] = $volume->modifier ? 0.5 : 1; | ||||
|             print "Reloading object $volume_idx, $instance_idx\n"; | ||||
|             push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( | ||||
|                 bounding_box    => $mesh->bounding_box, | ||||
|                 color           => $color, | ||||
|             ); | ||||
|             $v->composite_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx); | ||||
|             if ($self->select_by eq 'object') { | ||||
|                 $v->select_group_id($obj_idx*1000000); | ||||
|             } elsif ($self->select_by eq 'volume') { | ||||
|                 $v->select_group_id($obj_idx*1000000 + $volume_idx*1000); | ||||
|             } elsif ($self->select_by eq 'instance') { | ||||
|                 $v->select_group_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx); | ||||
|                 $v->select_group_id($v->composite_id); | ||||
|             } | ||||
|             if ($self->drag_by eq 'object') { | ||||
|                 $v->drag_group_id($obj_idx*1000); | ||||
|  | @ -1191,15 +1643,18 @@ sub load_object { | |||
|                 $v->drag_group_id($obj_idx*1000 + $instance_idx); | ||||
|             } | ||||
|             push @volumes_idx, my $scene_volume_idx = $#{$self->volumes}; | ||||
|             $self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $volume_idx, $instance_idx ]; | ||||
|              | ||||
|             my $verts = Slic3r::GUI::_3DScene::GLVertexArray->new; | ||||
|             $verts->load_mesh($mesh); | ||||
|             $v->tverts($verts); | ||||
| 
 | ||||
|             if (! $volume->modifier) { | ||||
|                 $v->layer_height_texture_data($layer_height_texture_data); | ||||
|                 $v->layer_height_texture_cells($layer_height_texture_cells); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     $self->volumes_by_object->{$obj_idx} = [@volumes_idx]; | ||||
|     return @volumes_idx; | ||||
| } | ||||
| 
 | ||||
|  | @ -1538,19 +1993,4 @@ sub _extrusionentity_to_verts { | |||
|         $tverts); | ||||
| } | ||||
| 
 | ||||
| sub object_idx { | ||||
|     my ($self, $volume_idx) = @_; | ||||
|     return $self->_objects_by_volumes->{$volume_idx}[0]; | ||||
| } | ||||
| 
 | ||||
| sub volume_idx { | ||||
|     my ($self, $volume_idx) = @_; | ||||
|     return $self->_objects_by_volumes->{$volume_idx}[1]; | ||||
| } | ||||
| 
 | ||||
| sub instance_idx { | ||||
|     my ($self, $volume_idx) = @_; | ||||
|     return $self->_objects_by_volumes->{$volume_idx}[2]; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  |  | |||
							
								
								
									
										184
									
								
								lib/Slic3r/GUI/GLShader.pm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								lib/Slic3r/GUI/GLShader.pm
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,184 @@ | |||
| ############################################################ | ||||
| # | ||||
| # Stripped down from the Perl OpenGL::Shader package by Vojtech Bubnik | ||||
| # to only support the GLSL shaders. The original source was not maintained | ||||
| # and did not install properly through the CPAN archive, and it was unnecessary | ||||
| # complex. | ||||
| # | ||||
| # Original copyright: | ||||
| # | ||||
| # Copyright 2007 Graphcomp - ALL RIGHTS RESERVED | ||||
| # Author: Bob "grafman" Free - grafman@graphcomp.com | ||||
| # | ||||
| # This program is free software; you can redistribute it and/or | ||||
| # modify it under the same terms as Perl itself. | ||||
| # | ||||
| ############################################################ | ||||
| 
 | ||||
| package Slic3r::GUI::GLShader; | ||||
| use OpenGL(':all'); | ||||
| 
 | ||||
| # Avoid cloning this class by the worker threads. | ||||
| sub CLONE_SKIP { 1 } | ||||
| 
 | ||||
| # Shader constructor | ||||
| sub new | ||||
| { | ||||
|   # Check for required OpenGL extensions | ||||
|   return undef if (OpenGL::glpCheckExtension('GL_ARB_shader_objects')); | ||||
|   return undef if (OpenGL::glpCheckExtension('GL_ARB_fragment_shader')); | ||||
|   return undef if (OpenGL::glpCheckExtension('GL_ARB_vertex_shader')); | ||||
|   return undef if (OpenGL::glpCheckExtension('GL_ARB_shading_language_100')); | ||||
| #  my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION); | ||||
| #  my $glsl_version_ARB = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB ); | ||||
| #  print "GLSL version: $glsl_version, ARB: $glsl_version_ARB\n"; | ||||
| 
 | ||||
|   my $this = shift; | ||||
|   my $class = ref($this) || $this; | ||||
|   my($type) = @_; | ||||
|   my $self = {type => uc($type)}; | ||||
|   bless($self,$class); | ||||
| 
 | ||||
|   # Get GL_SHADING_LANGUAGE_VERSION_ARB | ||||
|   my $shader_ver = glGetString(0x8B8C); | ||||
|   $shader_ver =~ m|([\d\.]+)|; | ||||
|   $self->{version} = $1 || '0'; | ||||
|   print  | ||||
|   return $self; | ||||
| } | ||||
| 
 | ||||
| # Shader destructor | ||||
| # Must be disabled first | ||||
| sub DESTROY | ||||
| { | ||||
|   my($self) = @_; | ||||
| 
 | ||||
|   if ($self->{program}) | ||||
|   { | ||||
|     glDetachObjectARB($self->{program},$self->{fragment_id}) if ($self->{fragment_id}); | ||||
|     glDetachObjectARB($self->{program},$self->{vertex_id}) if ($self->{vertex_id}); | ||||
|     glDeleteProgramsARB_p($self->{program}); | ||||
|   } | ||||
| 
 | ||||
|   glDeleteProgramsARB_p($self->{fragment_id}) if ($self->{fragment_id}); | ||||
|   glDeleteProgramsARB_p($self->{vertex_id}) if ($self->{vertex_id}); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| # Load shader strings | ||||
| sub Load | ||||
| { | ||||
|   my($self,$fragment,$vertex) = @_; | ||||
| 
 | ||||
|   # Load fragment code | ||||
|   if ($fragment) | ||||
|   { | ||||
|     $self->{fragment_id} = glCreateShaderObjectARB(GL_FRAGMENT_SHADER); | ||||
|     return undef if (!$self->{fragment_id}); | ||||
| 
 | ||||
|     glShaderSourceARB_p($self->{fragment_id}, $fragment); | ||||
|     #my $frag = glGetShaderSourceARB_p($self->{fragment_id}); | ||||
|     #print STDERR "Loaded fragment:\n$frag\n"; | ||||
| 
 | ||||
|     glCompileShaderARB($self->{fragment_id}); | ||||
|     my $stat = glGetInfoLogARB_p($self->{fragment_id}); | ||||
|     return "Fragment shader: $stat" if ($stat); | ||||
|   } | ||||
| 
 | ||||
|   # Load vertex code | ||||
|   if ($vertex) | ||||
|   { | ||||
|     $self->{vertex_id} = glCreateShaderObjectARB(GL_VERTEX_SHADER); | ||||
|     return undef if (!$self->{vertex_id}); | ||||
| 
 | ||||
|     glShaderSourceARB_p($self->{vertex_id}, $vertex); | ||||
|     #my $vert = glGetShaderSourceARB_p($self->{vertex_id}); | ||||
|     #print STDERR "Loaded vertex:\n$vert\n"; | ||||
| 
 | ||||
|     glCompileShaderARB($self->{vertex_id}); | ||||
|     $stat = glGetInfoLogARB_p($self->{vertex_id}); | ||||
|     return "Vertex shader: $stat" if ($stat); | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   # Link shaders | ||||
|   my $sp = glCreateProgramObjectARB(); | ||||
|   glAttachObjectARB($sp, $self->{fragment_id}) if ($fragment); | ||||
|   glAttachObjectARB($sp, $self->{vertex_id}) if ($vertex); | ||||
|   glLinkProgramARB($sp); | ||||
|   my $linked = glGetObjectParameterivARB_p($sp, GL_OBJECT_LINK_STATUS_ARB); | ||||
|   if (!$linked) | ||||
|   { | ||||
|     $stat = glGetInfoLogARB_p($sp); | ||||
|     #print STDERR "Load shader: $stat\n"; | ||||
|     return "Link shader: $stat" if ($stat); | ||||
|     return 'Unable to link shader'; | ||||
|   } | ||||
| 
 | ||||
|   $self->{program} = $sp; | ||||
| 
 | ||||
|   return ''; | ||||
| } | ||||
| 
 | ||||
| # Enable shader | ||||
| sub Enable | ||||
| { | ||||
|   my($self) = @_; | ||||
|   glUseProgramObjectARB($self->{program}) if ($self->{program}); | ||||
| } | ||||
| 
 | ||||
| # Disable shader | ||||
| sub Disable | ||||
| { | ||||
|   my($self) = @_; | ||||
|   glUseProgramObjectARB(0) if ($self->{program}); | ||||
| } | ||||
| 
 | ||||
| # Return shader vertex attribute ID | ||||
| sub MapAttr | ||||
| { | ||||
|   my($self,$attr) = @_; | ||||
|   return undef if (!$self->{program}); | ||||
|   my $id = glGetAttribLocationARB_p($self->{program},$attr); | ||||
|   return undef if ($id < 0); | ||||
|   return $id; | ||||
| } | ||||
| 
 | ||||
| # Return shader uniform variable ID | ||||
| sub Map | ||||
| { | ||||
|   my($self,$var) = @_; | ||||
|   return undef if (!$self->{program}); | ||||
|   my $id = glGetUniformLocationARB_p($self->{program},$var); | ||||
|   return undef if ($id < 0); | ||||
|   return $id; | ||||
| } | ||||
| 
 | ||||
| # Set shader vector | ||||
| sub SetVector | ||||
| { | ||||
|   my($self,$var,@values) = @_; | ||||
| 
 | ||||
|   my $id = $self->Map($var); | ||||
|   return 'Unable to map $var' if (!defined($id)); | ||||
| 
 | ||||
|   my $count = scalar(@values); | ||||
|   eval('glUniform'.$count.'fARB($id,@values)'); | ||||
| 
 | ||||
|   return ''; | ||||
| } | ||||
| 
 | ||||
| # Set shader 4x4 matrix | ||||
| sub SetMatrix | ||||
| { | ||||
|   my($self,$var,$oga) = @_; | ||||
| 
 | ||||
|   my $id = $self->Map($var); | ||||
|   return 'Unable to map $var' if (!defined($id)); | ||||
| 
 | ||||
|   glUniformMatrix4fvARB_c($id,1,0,$oga->ptr()); | ||||
|   return ''; | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
| __END__ | ||||
|  | @ -42,6 +42,11 @@ sub new { | |||
|     $self->_init_tabpanel; | ||||
|     $self->_init_menubar; | ||||
|      | ||||
|     # set default tooltip timer in msec | ||||
|     # SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values | ||||
|     # (SetAutoPop is not available on GTK.) | ||||
|     eval { Wx::ToolTip::SetAutoPop(32767) }; | ||||
|      | ||||
|     # initialize status bar | ||||
|     $self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1); | ||||
|     $self->{statusbar}->SetStatusText("Version $Slic3r::VERSION - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"); | ||||
|  | @ -292,7 +297,7 @@ sub _init_menubar { | |||
|     # View menu | ||||
|     if (!$self->{no_plater}) { | ||||
|         $self->{viewMenu} = Wx::Menu->new; | ||||
|         $self->_append_menu_item($self->{viewMenu}, "Default", 'Default View', sub { $self->select_view('default'); }); | ||||
|         $self->_append_menu_item($self->{viewMenu}, "Iso"    , 'Iso View'    , sub { $self->select_view('iso'    ); }); | ||||
|         $self->_append_menu_item($self->{viewMenu}, "Top"    , 'Top View'    , sub { $self->select_view('top'    ); }); | ||||
|         $self->_append_menu_item($self->{viewMenu}, "Bottom" , 'Bottom View' , sub { $self->select_view('bottom' ); }); | ||||
|         $self->_append_menu_item($self->{viewMenu}, "Front"  , 'Front View'  , sub { $self->select_view('front'  ); }); | ||||
|  |  | |||
|  | @ -8,10 +8,11 @@ use utf8; | |||
| use File::Basename qw(basename dirname); | ||||
| use List::Util qw(sum first max); | ||||
| use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad); | ||||
| use LWP::UserAgent; | ||||
| use threads::shared qw(shared_clone); | ||||
| use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc  | ||||
|     :panel :sizer :toolbar :window wxTheApp :notebook :combobox); | ||||
| use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED  | ||||
|     :panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap); | ||||
| use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED  | ||||
|     EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL  | ||||
|     EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED); | ||||
| use base 'Wx::Panel'; | ||||
|  | @ -30,6 +31,7 @@ use constant TB_SCALE   => &Wx::NewId; | |||
| use constant TB_SPLIT   => &Wx::NewId; | ||||
| use constant TB_CUT     => &Wx::NewId; | ||||
| use constant TB_SETTINGS => &Wx::NewId; | ||||
| use constant TB_LAYER_EDITING => &Wx::NewId; | ||||
| 
 | ||||
| # package variables to avoid passing lexicals to threads | ||||
| our $THUMBNAIL_DONE_EVENT    : shared = Wx::NewEventType; | ||||
|  | @ -94,7 +96,7 @@ sub new { | |||
|      | ||||
|     # Initialize 3D plater | ||||
|     if ($Slic3r::GUI::have_OpenGL) { | ||||
|         $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config}); | ||||
|         $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config}); | ||||
|         $self->{preview_notebook}->AddPage($self->{canvas3D}, '3D'); | ||||
|         $self->{canvas3D}->set_on_select_object($on_select_object); | ||||
|         $self->{canvas3D}->set_on_double_click($on_double_click); | ||||
|  | @ -154,6 +156,10 @@ sub new { | |||
|         $self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), ''); | ||||
|         $self->{htoolbar}->AddSeparator; | ||||
|         $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); | ||||
| 
 | ||||
|         # FIXME add a button for layer editing | ||||
|         $self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, undef, 'Layer Editing') | ||||
|             if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1); | ||||
|     } else { | ||||
|         my %tbar_buttons = ( | ||||
|             add             => "Add…", | ||||
|  | @ -168,12 +174,17 @@ sub new { | |||
|             split           => "Split", | ||||
|             cut             => "Cut…", | ||||
|             settings        => "Settings…", | ||||
|             layer_editing   => "Layer editing", | ||||
|         ); | ||||
|         $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL); | ||||
|         for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) { | ||||
|             $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); | ||||
|             $self->{btoolbar}->Add($self->{"btn_$_"}); | ||||
|         } | ||||
|         if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1) { | ||||
|             $self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); | ||||
|             $self->{btoolbar}->Add($self->{"btn_layer_editing"}); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     $self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize, | ||||
|  | @ -256,6 +267,11 @@ sub new { | |||
|         EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; }); | ||||
|         EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog }); | ||||
|         EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog }); | ||||
|         EVT_TOOL($self, TB_LAYER_EDITING, sub { | ||||
|             my $state = $self->{canvas3D}->layer_editing_enabled; | ||||
|             $self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state); | ||||
|             $self->on_layer_editing_toggled(! $state); | ||||
|         }); | ||||
|     } else { | ||||
|         EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; }); | ||||
|         EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove | ||||
|  | @ -269,6 +285,7 @@ sub new { | |||
|         EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; }); | ||||
|         EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog }); | ||||
|         EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog }); | ||||
|         EVT_TOGGLEBUTTON($self, $self->{btn_layer_editing}, sub { $self->on_layer_editing_toggled($self->{btn_layer_editing}->GetValue); }); | ||||
|     } | ||||
|      | ||||
|     $_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self)) | ||||
|  | @ -464,6 +481,12 @@ sub _on_select_preset { | |||
| 	$self->on_config_change($self->GetFrame->config); | ||||
| } | ||||
| 
 | ||||
| sub on_layer_editing_toggled { | ||||
|     my ($self, $new_state) = @_; | ||||
|     $self->{canvas3D}->layer_editing_enabled($new_state); | ||||
|     $self->{canvas3D}->update; | ||||
| } | ||||
| 
 | ||||
| sub GetFrame { | ||||
|     my ($self) = @_; | ||||
|     return &Wx::GetTopLevelParent($self); | ||||
|  | @ -1482,6 +1505,8 @@ sub on_thumbnail_made { | |||
| sub update { | ||||
|     my ($self, $force_autocenter) = @_; | ||||
| 
 | ||||
|     print "Platter - update\n"; | ||||
|      | ||||
|     if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) { | ||||
|         $self->{model}->center_instances_around_point($self->bed_centerf); | ||||
|     } | ||||
|  | @ -1679,7 +1704,7 @@ sub object_list_changed { | |||
|     my $have_objects = @{$self->{objects}} ? 1 : 0; | ||||
|     my $method = $have_objects ? 'Enable' : 'Disable'; | ||||
|     $self->{"btn_$_"}->$method | ||||
|         for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode); | ||||
|         for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode layer_editing); | ||||
|      | ||||
|     if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) { | ||||
|         $self->{btn_reslice}->Disable; | ||||
|  | @ -1712,6 +1737,7 @@ sub selection_changed { | |||
|     if ($self->{object_info_size}) { # have we already loaded the info pane? | ||||
|         if ($have_sel) { | ||||
|             my $model_object = $self->{model}->objects->[$obj_idx]; | ||||
|             $model_object->print_info; | ||||
|             my $model_instance = $model_object->instances->[0]; | ||||
|             $self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size})); | ||||
|             $self->{object_info_materials}->SetLabel($model_object->materials_count); | ||||
|  |  | |||
|  | @ -173,7 +173,7 @@ sub repaint { | |||
|              | ||||
|             # if sequential printing is enabled and we have more than one object, draw clearance area | ||||
|             if ($self->{config}->complete_objects && (map @{$_->instances}, @{$self->{model}->objects}) > 1) { | ||||
|                 my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), 1, JT_ROUND, scale(0.1))}; | ||||
|                 my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), JT_ROUND, scale(0.1))}; | ||||
|                 $dc->SetPen($self->{clearance_pen}); | ||||
|                 $dc->SetBrush($self->{transparent_brush}); | ||||
|                 $dc->DrawPolygon($self->scaled_points_to_pixel($clearance, 1), 0, 0); | ||||
|  | @ -185,7 +185,7 @@ sub repaint { | |||
|     if (@{$self->{objects}} && $self->{config}->skirts) { | ||||
|         my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$self->{objects}}; | ||||
|         if (@points >= 3) { | ||||
|             my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), 1, JT_ROUND, scale(0.1))}; | ||||
|             my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), JT_ROUND, scale(0.1))}; | ||||
|             $dc->SetPen($self->{skirt_pen}); | ||||
|             $dc->SetBrush($self->{transparent_brush}); | ||||
|             $dc->DrawPolygon($self->scaled_points_to_pixel($convex_hull, 1), 0, 0); | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ use base qw(Slic3r::GUI::3DScene Class::Accessor); | |||
| 
 | ||||
| sub new { | ||||
|     my $class = shift; | ||||
|     my ($parent, $objects, $model, $config) = @_; | ||||
|     my ($parent, $objects, $model, $print, $config) = @_; | ||||
|      | ||||
|     my $self = $class->SUPER::new($parent); | ||||
|     $self->enable_picking(1); | ||||
|  | @ -22,6 +22,7 @@ sub new { | |||
|      | ||||
|     $self->{objects}            = $objects; | ||||
|     $self->{model}              = $model; | ||||
|     $self->{print}              = $print; | ||||
|     $self->{config}             = $config; | ||||
|     $self->{on_select_object}   = sub {}; | ||||
|     $self->{on_instances_moved} = sub {}; | ||||
|  | @ -31,7 +32,7 @@ sub new { | |||
|          | ||||
|         my $obj_idx = undef; | ||||
|         if ($volume_idx != -1) { | ||||
|             $obj_idx = $self->object_idx($volume_idx); | ||||
|             $obj_idx = $self->volumes->[$volume_idx]->object_idx; | ||||
|         } | ||||
|         $self->{on_select_object}->($obj_idx) | ||||
|             if $self->{on_select_object}; | ||||
|  | @ -42,8 +43,8 @@ sub new { | |||
|         my %done = ();  # prevent moving instances twice | ||||
|         foreach my $volume_idx (@volume_idxs) { | ||||
|             my $volume = $self->volumes->[$volume_idx]; | ||||
|             my $obj_idx = $self->object_idx($volume_idx); | ||||
|             my $instance_idx = $self->instance_idx($volume_idx); | ||||
|             my $obj_idx = $volume->object_idx; | ||||
|             my $instance_idx = $volume->instance_idx; | ||||
|             next if $done{"${obj_idx}_${instance_idx}"}; | ||||
|             $done{"${obj_idx}_${instance_idx}"} = 1; | ||||
|              | ||||
|  | @ -89,7 +90,7 @@ sub update { | |||
|     $self->update_bed_size; | ||||
|      | ||||
|     foreach my $obj_idx (0..$#{$self->{model}->objects}) { | ||||
|         my @volume_idxs = $self->load_object($self->{model}, $obj_idx); | ||||
|         my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx); | ||||
|          | ||||
|         if ($self->{objects}[$obj_idx]->selected) { | ||||
|             $self->select_volume($_) for @volume_idxs; | ||||
|  |  | |||
|  | @ -114,7 +114,7 @@ sub new { | |||
|     my $canvas; | ||||
|     if ($Slic3r::GUI::have_OpenGL) { | ||||
|         $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); | ||||
|         $canvas->load_object($self->{model_object}, undef, [0]); | ||||
|         $canvas->load_object($self->{model_object}, undef, undef, [0]); | ||||
|         $canvas->set_auto_bed_shape; | ||||
|         $canvas->SetSize([500,500]); | ||||
|         $canvas->SetMinSize($canvas->GetSize); | ||||
|  | @ -244,7 +244,7 @@ sub _update { | |||
|             } | ||||
|              | ||||
|             $self->{canvas}->reset_objects; | ||||
|             $self->{canvas}->load_object($_, undef, [0]) for @objects; | ||||
|             $self->{canvas}->load_object($_, undef, undef, [0]) for @objects; | ||||
|             $self->{canvas}->SetCuttingPlane( | ||||
|                 $self->{cut_options}{z}, | ||||
|                 [@expolygons], | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ sub new { | |||
|     my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); | ||||
|      | ||||
|     my $object = $self->{model_object} = $params{model_object}; | ||||
|     my $print_object = $self->{print_object} = $params{print_object}; | ||||
|      | ||||
|     # create TreeCtrl | ||||
|     my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100],  | ||||
|  | @ -82,7 +83,7 @@ sub new { | |||
|             $self->reload_tree($canvas->volume_idx($volume_idx)); | ||||
|         }); | ||||
|          | ||||
|         $canvas->load_object($self->{model_object}, undef, [0]); | ||||
|         $canvas->load_object($self->{model_object}, undef, undef, [0]); | ||||
|         $canvas->set_auto_bed_shape; | ||||
|         $canvas->SetSize([500,500]); | ||||
|         $canvas->zoom_to_volumes; | ||||
|  |  | |||
|  | @ -523,6 +523,7 @@ sub build { | |||
|         max_volumetric_extrusion_rate_slope_positive max_volumetric_extrusion_rate_slope_negative | ||||
|         perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed  | ||||
|         solid_infill_speed top_solid_infill_speed support_material_speed  | ||||
|         support_material_xy_spacing | ||||
|         support_material_interface_speed bridge_speed gap_fill_speed | ||||
|         travel_speed | ||||
|         first_layer_speed | ||||
|  | @ -532,8 +533,8 @@ sub build { | |||
|         brim_width | ||||
|         support_material support_material_threshold support_material_enforce_layers | ||||
|         raft_layers | ||||
|         support_material_pattern support_material_with_sheath support_material_spacing support_material_angle | ||||
|         support_material_interface_layers support_material_interface_spacing | ||||
|         support_material_pattern support_material_with_sheath support_material_spacing support_material_synchronize_layers support_material_angle | ||||
|         support_material_interface_layers support_material_interface_spacing support_material_interface_contact_loops | ||||
|         support_material_contact_distance support_material_buildplate_only dont_support_bridges | ||||
|         notes | ||||
|         complete_objects extruder_clearance_radius extruder_clearance_height | ||||
|  | @ -646,8 +647,11 @@ sub build { | |||
|             $optgroup->append_single_option_line('support_material_angle'); | ||||
|             $optgroup->append_single_option_line('support_material_interface_layers'); | ||||
|             $optgroup->append_single_option_line('support_material_interface_spacing'); | ||||
|             $optgroup->append_single_option_line('support_material_interface_contact_loops'); | ||||
|             $optgroup->append_single_option_line('support_material_buildplate_only'); | ||||
|             $optgroup->append_single_option_line('support_material_xy_spacing'); | ||||
|             $optgroup->append_single_option_line('dont_support_bridges'); | ||||
|             $optgroup->append_single_option_line('support_material_synchronize_layers'); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|  | @ -910,12 +914,12 @@ sub _update { | |||
|     my $have_support_interface = $config->support_material_interface_layers > 0; | ||||
|     $self->get_field($_)->toggle($have_support_material) | ||||
|         for qw(support_material_threshold support_material_pattern support_material_with_sheath | ||||
|             support_material_spacing support_material_angle | ||||
|             support_material_spacing support_material_synchronize_layers support_material_angle | ||||
|             support_material_interface_layers dont_support_bridges | ||||
|             support_material_extrusion_width support_material_contact_distance); | ||||
|             support_material_extrusion_width support_material_contact_distance support_material_xy_spacing); | ||||
|     $self->get_field($_)->toggle($have_support_material && $have_support_interface) | ||||
|         for qw(support_material_interface_spacing support_material_interface_extruder | ||||
|             support_material_interface_speed); | ||||
|             support_material_interface_speed support_material_interface_contact_loops); | ||||
|      | ||||
|     $self->get_field('perimeter_extrusion_width')->toggle($have_perimeters || $have_skirt || $have_brim); | ||||
|     $self->get_field('support_material_extruder')->toggle($have_support_material || $have_skirt); | ||||
|  |  | |||
|  | @ -5,9 +5,9 @@ use warnings; | |||
| require Exporter; | ||||
| our @ISA = qw(Exporter); | ||||
| our @EXPORT_OK = qw(offset offset_ex | ||||
|     diff_ex diff union_ex intersection_ex xor_ex JT_ROUND JT_MITER | ||||
|     JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex | ||||
|     intersection intersection_pl diff_pl union CLIPPER_OFFSET_SCALE | ||||
|     union_pt_chained diff_ppl intersection_ppl); | ||||
|     diff_ex diff union_ex intersection_ex JT_ROUND JT_MITER | ||||
|     JT_SQUARE is_counter_clockwise offset2 offset2_ex | ||||
|     intersection intersection_pl diff_pl union | ||||
|     union_pt_chained); | ||||
| 
 | ||||
| 1; | ||||
|  |  | |||
|  | @ -41,8 +41,12 @@ sub size { | |||
| sub process { | ||||
|     my ($self) = @_; | ||||
|      | ||||
|     $self->status_cb->(20, "Generating perimeters"); | ||||
|     $_->make_perimeters for @{$self->objects}; | ||||
|      | ||||
|     $self->status_cb->(70, "Infilling layers"); | ||||
|     $_->infill for @{$self->objects}; | ||||
|      | ||||
|     $_->generate_support_material for @{$self->objects}; | ||||
|     $self->make_skirt; | ||||
|     $self->make_brim;  # must come after make_skirt | ||||
|  | @ -276,7 +280,7 @@ sub make_skirt { | |||
|     my $distance = scale max($self->config->skirt_distance, $self->config->brim_width); | ||||
|     for (my $i = $skirts; $i > 0; $i--) { | ||||
|         $distance += scale $spacing; | ||||
|         my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0]; | ||||
|         my $loop = offset([$convex_hull], $distance, JT_ROUND, scale(0.1))->[0]; | ||||
|         my $eloop = Slic3r::ExtrusionLoop->new_from_paths( | ||||
|             Slic3r::ExtrusionPath->new( | ||||
|                 polyline        => Slic3r::Polygon->new(@$loop)->split_at_first_point, | ||||
|  | @ -369,7 +373,7 @@ sub make_brim { | |||
|         # -0.5 because islands are not represented by their centerlines | ||||
|         # (first offset more, then step back - reverse order than the one used for  | ||||
|         # perimeters because here we're offsetting outwards) | ||||
|         push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, 100000, JT_SQUARE)}; | ||||
|         push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, JT_SQUARE)}; | ||||
|     } | ||||
|      | ||||
|     $self->brim->append(map Slic3r::ExtrusionLoop->new_from_paths( | ||||
|  |  | |||
|  | @ -262,7 +262,10 @@ sub export { | |||
|                     $gcodegen->avoid_crossing_perimeters->set_disable_once(1); | ||||
|                 } | ||||
|                  | ||||
|                 my @layers = sort { $a->print_z <=> $b->print_z } @{$object->layers}, @{$object->support_layers}; | ||||
|                 # Order layers by print_z, support layers preceding the object layers. | ||||
|                 my @layers = sort  | ||||
|                     { ($a->print_z == $b->print_z) ? ($a->isa('Slic3r::Layer::Support') ? -1 : 0) : $a->print_z <=> $b->print_z } | ||||
|                     @{$object->layers}, @{$object->support_layers}; | ||||
|                 for my $layer (@layers) { | ||||
|                     # if we are printing the bottom layer of an object, and we have already finished | ||||
|                     # another one, set first layer temperatures. this happens before the Z move | ||||
|  | @ -289,7 +292,8 @@ sub export { | |||
|         my %layers = ();  # print_z => [ [layers], [layers], [layers] ]  by obj_idx | ||||
|         foreach my $obj_idx (0 .. ($self->print->object_count - 1)) { | ||||
|             my $object = $self->objects->[$obj_idx]; | ||||
|             foreach my $layer (@{$object->layers}, @{$object->support_layers}) { | ||||
|             # Collect the object layers by z, support layers first, object layers second. | ||||
|             foreach my $layer (@{$object->support_layers}, @{$object->layers}) { | ||||
|                 $layers{ $layer->print_z } ||= []; | ||||
|                 $layers{ $layer->print_z }[$obj_idx] ||= []; | ||||
|                 push @{$layers{ $layer->print_z }[$obj_idx]}, $layer; | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ use List::Util qw(min max sum first); | |||
| use Slic3r::Flow ':roles'; | ||||
| use Slic3r::Geometry qw(X Y Z PI scale unscale chained_path epsilon); | ||||
| use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex  | ||||
|     offset offset_ex offset2 offset2_ex intersection_ppl CLIPPER_OFFSET_SCALE JT_MITER); | ||||
|     offset offset_ex offset2 offset2_ex JT_MITER); | ||||
| use Slic3r::Print::State ':steps'; | ||||
| use Slic3r::Surface ':types'; | ||||
| 
 | ||||
|  | @ -45,223 +45,7 @@ sub slice { | |||
|     $self->set_step_started(STEP_SLICE); | ||||
|     $self->print->status_cb->(10, "Processing triangulated mesh"); | ||||
|      | ||||
|     # init layers | ||||
|     { | ||||
|         $self->clear_layers; | ||||
|      | ||||
|         # make layers taking custom heights into account | ||||
|         my $id      = 0; | ||||
|         my $print_z = 0; | ||||
|         my $first_object_layer_height   = -1; | ||||
|         my $first_object_layer_distance = -1; | ||||
|      | ||||
|         # add raft layers | ||||
|         if ($self->config->raft_layers > 0) { | ||||
|             # Reserve object layers for the raft. Last layer of the raft is the contact layer. | ||||
|             $id += $self->config->raft_layers; | ||||
|          | ||||
|             # Raise first object layer Z by the thickness of the raft itself | ||||
|             # plus the extra distance required by the support material logic. | ||||
|             #FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case. | ||||
|             my $first_layer_height = $self->config->get_value('first_layer_height'); | ||||
|             $print_z += $first_layer_height; | ||||
|              | ||||
|             # Use as large as possible layer height for the intermediate raft layers. | ||||
|             my $support_material_layer_height; | ||||
|             { | ||||
|                 my @nozzle_diameters = ( | ||||
|                     map $self->print->config->get_at('nozzle_diameter', $_), | ||||
|                         $self->config->support_material_extruder-1, | ||||
|                         $self->config->support_material_interface_extruder-1, | ||||
|                 ); | ||||
|                 $support_material_layer_height = 0.75 * min(@nozzle_diameters); | ||||
|             } | ||||
|             $print_z += $support_material_layer_height * ($self->config->raft_layers - 1); | ||||
|          | ||||
|             # compute the average of all nozzles used for printing the object | ||||
|             #FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa? | ||||
|             my $nozzle_diameter; | ||||
|             { | ||||
|                 my @nozzle_diameters = ( | ||||
|                     map $self->print->config->get_at('nozzle_diameter', $_), @{$self->print->object_extruders} | ||||
|                 ); | ||||
|                 $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters; | ||||
|             } | ||||
|             $first_object_layer_distance = $self->_support_material->contact_distance($self->config->layer_height, $nozzle_diameter); | ||||
|          | ||||
|             # force first layer print_z according to the contact distance | ||||
|             # (the loop below will raise print_z by such height) | ||||
|             $first_object_layer_height = $first_object_layer_distance - $self->config->support_material_contact_distance; | ||||
|         } | ||||
|      | ||||
|         # loop until we have at least one layer and the max slice_z reaches the object height | ||||
|         my $slice_z = 0; | ||||
|         my $height  = 0; | ||||
|         my $max_z   = unscale($self->size->z); | ||||
|         while (($slice_z - $height) <= $max_z) { | ||||
|             # assign the default height to the layer according to the general settings | ||||
|             $height = ($id == 0) | ||||
|                 ? $self->config->get_value('first_layer_height') | ||||
|                 : $self->config->layer_height; | ||||
|          | ||||
|             # look for an applicable custom range | ||||
|             if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) { | ||||
|                 $height = $range->[2]; | ||||
|          | ||||
|                 # if user set custom height to zero we should just skip the range and resume slicing over it | ||||
|                 if ($height == 0) { | ||||
|                     $slice_z += $range->[1] - $range->[0]; | ||||
|                     next; | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             if ($first_object_layer_height != -1 && !@{$self->layers}) { | ||||
|                 $height = $first_object_layer_height; | ||||
|                 $print_z += ($first_object_layer_distance - $height); | ||||
|             } | ||||
|              | ||||
|             $print_z += $height; | ||||
|             $slice_z += $height/2; | ||||
|          | ||||
|             ### Slic3r::debugf "Layer %d: height = %s; slice_z = %s; print_z = %s\n", $id, $height, $slice_z, $print_z; | ||||
|          | ||||
|             $self->add_layer($id, $height, $print_z, $slice_z); | ||||
|             if ($self->layer_count >= 2) { | ||||
|                 my $lc = $self->layer_count; | ||||
|                 $self->get_layer($lc - 2)->set_upper_layer($self->get_layer($lc - 1)); | ||||
|                 $self->get_layer($lc - 1)->set_lower_layer($self->get_layer($lc - 2)); | ||||
|             } | ||||
|             $id++; | ||||
|          | ||||
|             $slice_z += $height/2;   # add the other half layer | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     # make sure all layers contain layer region objects for all regions | ||||
|     my $regions_count = $self->print->region_count; | ||||
|     foreach my $layer (@{ $self->layers }) { | ||||
|         $layer->region($_) for 0 .. ($regions_count-1); | ||||
|     } | ||||
|      | ||||
|     # get array of Z coordinates for slicing | ||||
|     my @z = map $_->slice_z, @{$self->layers}; | ||||
|      | ||||
|     # slice all non-modifier volumes | ||||
|     for my $region_id (0..($self->region_count - 1)) { | ||||
|         my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 0); | ||||
|         for my $layer_id (0..$#$expolygons_by_layer) { | ||||
|             my $layerm = $self->get_layer($layer_id)->regions->[$region_id]; | ||||
|             $layerm->slices->clear; | ||||
|             foreach my $expolygon (@{ $expolygons_by_layer->[$layer_id] }) { | ||||
|                 $layerm->slices->append(Slic3r::Surface->new( | ||||
|                     expolygon    => $expolygon, | ||||
|                     surface_type => S_TYPE_INTERNAL, | ||||
|                 )); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     # then slice all modifier volumes | ||||
|     if ($self->region_count > 1) { | ||||
|         for my $region_id (0..$self->region_count) { | ||||
|             my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 1); | ||||
|              | ||||
|             # loop through the other regions and 'steal' the slices belonging to this one | ||||
|             for my $other_region_id (0..$self->region_count) { | ||||
|                 next if $other_region_id == $region_id; | ||||
|                  | ||||
|                 for my $layer_id (0..$#$expolygons_by_layer) { | ||||
|                     my $layerm = $self->get_layer($layer_id)->regions->[$region_id]; | ||||
|                     my $other_layerm = $self->get_layer($layer_id)->regions->[$other_region_id]; | ||||
|                     next if !defined $other_layerm; | ||||
|                      | ||||
|                     my $other_slices = [ map $_->p, @{$other_layerm->slices} ];  # Polygons | ||||
|                     my $my_parts = intersection_ex( | ||||
|                         $other_slices, | ||||
|                         [ map @$_, @{ $expolygons_by_layer->[$layer_id] } ], | ||||
|                     ); | ||||
|                     next if !@$my_parts; | ||||
|                      | ||||
|                     # append new parts to our region | ||||
|                     foreach my $expolygon (@$my_parts) { | ||||
|                         $layerm->slices->append(Slic3r::Surface->new( | ||||
|                             expolygon    => $expolygon, | ||||
|                             surface_type => S_TYPE_INTERNAL, | ||||
|                         )); | ||||
|                     } | ||||
|                      | ||||
|                     # remove such parts from original region | ||||
|                     $other_layerm->slices->clear; | ||||
|                     $other_layerm->slices->append(Slic3r::Surface->new( | ||||
|                         expolygon    => $_, | ||||
|                         surface_type => S_TYPE_INTERNAL, | ||||
|                     )) for @{ diff_ex($other_slices, [ map @$_, @$my_parts ]) }; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     # remove last layer(s) if empty | ||||
|     $self->delete_layer($self->layer_count - 1) | ||||
|         while $self->layer_count && (!map @{$_->slices}, @{$self->get_layer($self->layer_count - 1)->regions}); | ||||
|      | ||||
|     foreach my $layer (@{ $self->layers }) { | ||||
|         # apply size compensation | ||||
|         if ($self->config->xy_size_compensation != 0) { | ||||
|             my $delta = scale($self->config->xy_size_compensation); | ||||
|             if (@{$layer->regions} == 1) { | ||||
|                 # single region | ||||
|                 my $layerm = $layer->regions->[0]; | ||||
|                 my $slices = [ map $_->p, @{$layerm->slices} ]; | ||||
|                 $layerm->slices->clear; | ||||
|                 $layerm->slices->append(Slic3r::Surface->new( | ||||
|                     expolygon    => $_, | ||||
|                     surface_type => S_TYPE_INTERNAL, | ||||
|                 )) for @{offset_ex($slices, $delta)}; | ||||
|             } else { | ||||
|                 if ($delta < 0) { | ||||
|                     # multiple regions, shrinking | ||||
|                     # we apply the offset to the combined shape, then intersect it | ||||
|                     # with the original slices for each region | ||||
|                     my $slices = union([ map $_->p, map @{$_->slices}, @{$layer->regions} ]); | ||||
|                     $slices = offset($slices, $delta); | ||||
|                     foreach my $layerm (@{$layer->regions}) { | ||||
|                         my $this_slices = intersection_ex( | ||||
|                             $slices, | ||||
|                             [ map $_->p, @{$layerm->slices} ], | ||||
|                         ); | ||||
|                         $layerm->slices->clear; | ||||
|                         $layerm->slices->append(Slic3r::Surface->new( | ||||
|                             expolygon    => $_, | ||||
|                             surface_type => S_TYPE_INTERNAL, | ||||
|                         )) for @$this_slices; | ||||
|                     } | ||||
|                 } else { | ||||
|                     # multiple regions, growing | ||||
|                     # this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap | ||||
|                     # so we give priority to the first one and so on | ||||
|                     for my $i (0..$#{$layer->regions}) { | ||||
|                         my $layerm = $layer->regions->[$i]; | ||||
|                         my $slices = offset_ex([ map $_->p, @{$layerm->slices} ], $delta); | ||||
|                         if ($i > 0) { | ||||
|                             $slices = diff_ex( | ||||
|                                 [ map @$_, @$slices ], | ||||
|                                 [ map $_->p, map @{$_->slices}, map $layer->regions->[$_], 0..($i-1) ],  # slices of already processed regions | ||||
|                             ); | ||||
|                         } | ||||
|                         $layerm->slices->clear; | ||||
|                         $layerm->slices->append(Slic3r::Surface->new( | ||||
|                             expolygon    => $_, | ||||
|                             surface_type => S_TYPE_INTERNAL, | ||||
|                         )) for @$slices; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         # Merge all regions' slices to get islands, chain them by a shortest path. | ||||
|         $layer->make_slices; | ||||
|     } | ||||
|     $self->_slice; | ||||
|      | ||||
|     # detect slicing errors | ||||
|     my $warning_thrown = 0; | ||||
|  | @ -334,151 +118,15 @@ sub slice { | |||
|     $self->set_step_done(STEP_SLICE); | ||||
| } | ||||
| 
 | ||||
| # called from slice() | ||||
| sub _slice_region { | ||||
|     my ($self, $region_id, $z, $modifier) = @_; | ||||
| 
 | ||||
|     return [] if !@{$self->get_region_volumes($region_id)}; | ||||
| 
 | ||||
|     # compose mesh | ||||
|     my $mesh; | ||||
|     foreach my $volume_id (@{ $self->get_region_volumes($region_id) }) { | ||||
|         my $volume = $self->model_object->volumes->[$volume_id]; | ||||
|         next if $volume->modifier && !$modifier; | ||||
|         next if !$volume->modifier && $modifier; | ||||
|          | ||||
|         if (defined $mesh) { | ||||
|             $mesh->merge($volume->mesh); | ||||
|         } else { | ||||
|             $mesh = $volume->mesh->clone; | ||||
|         } | ||||
|     } | ||||
|     return if !defined $mesh; | ||||
| 
 | ||||
|     # transform mesh | ||||
|     # we ignore the per-instance transformations currently and only  | ||||
|     # consider the first one | ||||
|     $self->model_object->instances->[0]->transform_mesh($mesh, 1); | ||||
| 
 | ||||
|     # align mesh to Z = 0 (it should be already aligned actually) and apply XY shift | ||||
|     $mesh->translate((map unscale(-$_), @{$self->_copies_shift}), -$self->model_object->bounding_box->z_min); | ||||
|      | ||||
|     # perform actual slicing | ||||
|     return $mesh->slice($z); | ||||
| } | ||||
| 
 | ||||
| # 1) Merges typed region slices into stInternal type. | ||||
| # 2) Increases an "extra perimeters" counter at region slices where needed. | ||||
| # 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal). | ||||
| sub make_perimeters { | ||||
|     my $self = shift; | ||||
|     my ($self) = @_; | ||||
|      | ||||
|     # prerequisites | ||||
|     $self->slice; | ||||
|      | ||||
|     return if $self->step_done(STEP_PERIMETERS); | ||||
|     $self->set_step_started(STEP_PERIMETERS); | ||||
|     $self->print->status_cb->(20, "Generating perimeters"); | ||||
|      | ||||
|     # Merge region slices if they were split into types. | ||||
|     # FIXME this is using a safety offset, so the region slices will be slightly bigger with each iteration. | ||||
|     if ($self->typed_slices) { | ||||
|         $_->merge_slices for @{$self->layers}; | ||||
|         $self->set_typed_slices(0); | ||||
|         $self->invalidate_step(STEP_PREPARE_INFILL); | ||||
|     } | ||||
|      | ||||
|     # compare each layer to the one below, and mark those slices needing | ||||
|     # one additional inner perimeter, like the top of domed objects- | ||||
|      | ||||
|     # this algorithm makes sure that at least one perimeter is overlapping | ||||
|     # but we don't generate any extra perimeter if fill density is zero, as they would be floating | ||||
|     # inside the object - infill_only_where_needed should be the method of choice for printing | ||||
|     # hollow objects | ||||
|     for my $region_id (0 .. ($self->print->region_count-1)) { | ||||
|         my $region = $self->print->regions->[$region_id]; | ||||
|         my $region_perimeters = $region->config->perimeters; | ||||
|          | ||||
|         next if !$region->config->extra_perimeters; | ||||
|         next if $region_perimeters == 0; | ||||
|         next if $region->config->fill_density == 0; | ||||
|          | ||||
|         for my $i (0 .. ($self->layer_count - 2)) { | ||||
|             my $layerm                  = $self->get_layer($i)->get_region($region_id); | ||||
|             my $upper_layerm            = $self->get_layer($i+1)->get_region($region_id); | ||||
|             my $upper_layerm_polygons   = [ map $_->p, @{$upper_layerm->slices} ]; | ||||
|             # Filter upper layer polygons in intersection_ppl by their bounding boxes? | ||||
|             # my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; | ||||
|             my $total_loop_length       = sum(map $_->length, @$upper_layerm_polygons) // 0; | ||||
|              | ||||
|             my $perimeter_spacing       = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing; | ||||
|             my $ext_perimeter_flow      = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER); | ||||
|             my $ext_perimeter_width     = $ext_perimeter_flow->scaled_width; | ||||
|             my $ext_perimeter_spacing   = $ext_perimeter_flow->scaled_spacing; | ||||
|              | ||||
|             foreach my $slice (@{$layerm->slices}) { | ||||
|                 while (1) { | ||||
|                     # compute the total thickness of perimeters | ||||
|                     my $perimeters_thickness = $ext_perimeter_width/2 + $ext_perimeter_spacing/2 | ||||
|                         + ($region_perimeters-1 + $slice->extra_perimeters) * $perimeter_spacing; | ||||
|                      | ||||
|                     # define a critical area where we don't want the upper slice to fall into | ||||
|                     # (it should either lay over our perimeters or outside this area) | ||||
|                     my $critical_area_depth = $perimeter_spacing*1.5; | ||||
|                     my $critical_area = diff( | ||||
|                         offset($slice->expolygon->arrayref, -$perimeters_thickness), | ||||
|                         offset($slice->expolygon->arrayref, -($perimeters_thickness + $critical_area_depth)), | ||||
|                     ); | ||||
|                      | ||||
|                     # check whether a portion of the upper slices falls inside the critical area | ||||
|                     my $intersection = intersection_ppl( | ||||
|                         $upper_layerm_polygons, | ||||
|                         $critical_area, | ||||
|                     ); | ||||
|                      | ||||
|                     # only add an additional loop if at least 30% of the slice loop would benefit from it | ||||
|                     my $total_intersection_length = sum(map $_->length, @$intersection) // 0; | ||||
|                     last unless $total_intersection_length > $total_loop_length*0.3; | ||||
|                      | ||||
|                     if (0) { | ||||
|                         require "Slic3r/SVG.pm"; | ||||
|                         Slic3r::SVG::output( | ||||
|                             "extra.svg", | ||||
|                             no_arrows   => 1, | ||||
|                             expolygons  => union_ex($critical_area), | ||||
|                             polylines   => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], | ||||
|                         ); | ||||
|                     } | ||||
|                      | ||||
|                     $slice->extra_perimeters($slice->extra_perimeters + 1); | ||||
|                 } | ||||
|                 Slic3r::debugf "  adding %d more perimeter(s) at layer %d\n", | ||||
|                     $slice->extra_perimeters, $layerm->layer->id | ||||
|                     if $slice->extra_perimeters > 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     Slic3r::parallelize( | ||||
|         threads => $self->print->config->threads, | ||||
|         items => sub { 0 .. ($self->layer_count - 1) }, | ||||
|         thread_cb => sub { | ||||
|             my $q = shift; | ||||
|             while (defined (my $i = $q->dequeue)) { | ||||
|                 $self->get_layer($i)->make_perimeters; | ||||
|             } | ||||
|         }, | ||||
|         no_threads_cb => sub { | ||||
|             $_->make_perimeters for @{$self->layers}; | ||||
|         }, | ||||
|     ); | ||||
|      | ||||
|     # simplify slices (both layer and region slices), | ||||
|     # we only need the max resolution for perimeters | ||||
|     ### This makes this method not-idempotent, so we keep it disabled for now. | ||||
|     ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); | ||||
|      | ||||
|     $self->set_step_done(STEP_PERIMETERS); | ||||
|     $self->_make_perimeters; | ||||
| } | ||||
| 
 | ||||
| sub prepare_infill { | ||||
|  | @ -598,32 +246,7 @@ sub infill { | |||
|      | ||||
|     # prerequisites | ||||
|     $self->prepare_infill; | ||||
|      | ||||
|     return if $self->step_done(STEP_INFILL); | ||||
|     $self->set_step_started(STEP_INFILL); | ||||
|     $self->print->status_cb->(70, "Infilling layers"); | ||||
|      | ||||
|     Slic3r::parallelize( | ||||
|         threads => $self->print->config->threads, | ||||
|         items => sub { 0..$#{$self->layers} }, | ||||
|         thread_cb => sub { | ||||
|             my $q = shift; | ||||
|             while (defined (my $i = $q->dequeue)) { | ||||
|                 $self->get_layer($i)->make_fills; | ||||
|             } | ||||
|         }, | ||||
|         no_threads_cb => sub { | ||||
|             foreach my $layer (@{$self->layers}) { | ||||
|                 $layer->make_fills; | ||||
|             } | ||||
|         }, | ||||
|     ); | ||||
| 
 | ||||
|     ### we could free memory now, but this would make this step not idempotent | ||||
|     ### Vojtech: Cannot release the fill_surfaces, they are used by the support generator. | ||||
|     ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; | ||||
|      | ||||
|     $self->set_step_done(STEP_INFILL); | ||||
|     $self->_infill; | ||||
| } | ||||
| 
 | ||||
| sub generate_support_material { | ||||
|  | @ -637,20 +260,10 @@ sub generate_support_material { | |||
|      | ||||
|     $self->clear_support_layers; | ||||
|      | ||||
|     if ((!$self->config->support_material && $self->config->raft_layers == 0) || scalar(@{$self->layers}) < 2) { | ||||
|         $self->set_step_done(STEP_SUPPORTMATERIAL); | ||||
|         return; | ||||
|     } | ||||
|     if (($self->config->support_material || $self->config->raft_layers > 0) && scalar(@{$self->layers}) > 1) { | ||||
|         $self->print->status_cb->(85, "Generating support material");     | ||||
|      | ||||
|     $self->_support_material->generate($self); | ||||
|      | ||||
|     $self->set_step_done(STEP_SUPPORTMATERIAL); | ||||
| } | ||||
| 
 | ||||
| sub _support_material { | ||||
|     my ($self) = @_; | ||||
|      | ||||
|         if (0) { | ||||
|             # Old supports, Perl implementation. | ||||
|             my $first_layer_flow = Slic3r::Flow->new_from_width( | ||||
|                 width               => ($self->print->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width), | ||||
|                 role                => FLOW_ROLE_SUPPORT_MATERIAL, | ||||
|  | @ -659,20 +272,21 @@ sub _support_material { | |||
|                 layer_height        => $self->config->get_abs_value('first_layer_height'), | ||||
|                 bridge_flow_ratio   => 0, | ||||
|             );             | ||||
|      | ||||
|     if (1) { | ||||
|         # Old supports, Perl implementation. | ||||
|         return Slic3r::Print::SupportMaterial->new( | ||||
|             my $support_material = Slic3r::Print::SupportMaterial->new( | ||||
|                 print_config        => $self->print->config, | ||||
|                 object_config       => $self->config, | ||||
|                 first_layer_flow    => $first_layer_flow, | ||||
|                 flow                => $self->support_material_flow, | ||||
|                 interface_flow      => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), | ||||
|             ); | ||||
|             $support_material->generate($self); | ||||
|         } else { | ||||
|             # New supports, C++ implementation. | ||||
|         return Slic3r::Print::SupportMaterial2->new($self); | ||||
|             $self->_generate_support_material; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     $self->set_step_done(STEP_SUPPORTMATERIAL); | ||||
| } | ||||
| 
 | ||||
| # Idempotence of this method is guaranteed by the fact that we don't remove things from | ||||
|  | @ -863,7 +477,7 @@ sub discover_horizontal_shells { | |||
|                         # and it's not wanted in a hollow print even if it would make sense when | ||||
|                         # obeying the solid shell count option strictly (DWIM!) | ||||
|                         my $margin = $neighbor_layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width; | ||||
|                         my $regularized = offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5); | ||||
|                         my $regularized = offset2($new_internal_solid, -$margin, +$margin, JT_MITER, 5); | ||||
|                         my $too_narrow = diff( | ||||
|                             $new_internal_solid, | ||||
|                             $regularized, | ||||
|  | @ -893,7 +507,7 @@ sub discover_horizontal_shells { | |||
|                         # have the same angle, so the next shell would be grown even more and so on. | ||||
|                         my $too_narrow = diff( | ||||
|                             $new_internal_solid, | ||||
|                             offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5), | ||||
|                             offset2($new_internal_solid, -$margin, +$margin, JT_MITER, 5), | ||||
|                             1, | ||||
|                         ); | ||||
|                          | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ use Slic3r::ExtrusionPath ':roles'; | |||
| use Slic3r::Flow ':roles'; | ||||
| use Slic3r::Geometry qw(epsilon scale scaled_epsilon PI rad2deg deg2rad convex_hull); | ||||
| use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2 | ||||
|     intersection_pl offset2_ex diff_pl CLIPPER_OFFSET_SCALE JT_MITER JT_ROUND); | ||||
|     intersection_pl offset2_ex diff_pl JT_MITER JT_ROUND); | ||||
| use Slic3r::Surface ':types'; | ||||
| 
 | ||||
| has 'print_config'      => (is => 'rw', required => 1); | ||||
|  | @ -20,6 +20,7 @@ has 'interface_flow'    => (is => 'rw', required => 1); | |||
| use constant DEBUG_CONTACT_ONLY => 0; | ||||
| 
 | ||||
| # increment used to reach MARGIN in steps to avoid trespassing thin objects | ||||
| use constant MARGIN => 1.5; | ||||
| use constant MARGIN_STEP => MARGIN/3; | ||||
| 
 | ||||
| # generate a tree-like structure to save material | ||||
|  | @ -45,6 +46,7 @@ sub generate { | |||
|     # We now know the upper and lower boundaries for our support material object | ||||
|     # (@$contact_z and @$top_z), so we can generate intermediate layers. | ||||
|     my $support_z = $self->support_layers_z( | ||||
|         $object, | ||||
|         [ sort keys %$contact ], | ||||
|         [ sort keys %$top ], | ||||
|         max(map $_->height, @{$object->layers}) | ||||
|  | @ -299,9 +301,8 @@ sub contact_area { | |||
|                             offset( | ||||
|                                 $diff,  | ||||
|                                 $_, | ||||
|                                 CLIPPER_OFFSET_SCALE, | ||||
|                                 JT_ROUND, | ||||
|                                 scale(0.05)*CLIPPER_OFFSET_SCALE), | ||||
|                                 scale(0.05)), | ||||
|                             $slices_margin | ||||
|                         ); | ||||
|                     } | ||||
|  | @ -371,7 +372,7 @@ sub object_top { | |||
|                 # grow top surfaces so that interface and support generation are generated | ||||
|                 # with some spacing from object - it looks we don't need the actual | ||||
|                 # top shapes so this can be done here | ||||
|                 $top{ $layer->print_z } = offset($touching, $self->flow->scaled_width); | ||||
|                 $top{ $layer->print_z } = offset($touching, $self->flow->scaled_width + $self->object_config->support_material_xy_spacing); | ||||
|             } | ||||
|              | ||||
|             # remove the areas that touched from the projection that will continue on  | ||||
|  | @ -384,7 +385,7 @@ sub object_top { | |||
| } | ||||
| 
 | ||||
| sub support_layers_z { | ||||
|     my ($self, $contact_z, $top_z, $max_object_layer_height) = @_; | ||||
|     my ($self, $object, $contact_z, $top_z, $max_object_layer_height) = @_; | ||||
|      | ||||
|     # quick table to check whether a given Z is a top surface | ||||
|     my %top = map { $_ => 1 } @$top_z; | ||||
|  | @ -397,13 +398,18 @@ sub support_layers_z { | |||
|     my $contact_distance = $self->contact_distance($support_material_height, $nozzle_diameter); | ||||
|      | ||||
|     # initialize known, fixed, support layers | ||||
|     my @z = sort { $a <=> $b } | ||||
|         @$contact_z, | ||||
|     my @z = @$contact_z; | ||||
|     my $synchronize = $self->object_config->support_material_synchronize_layers; | ||||
|     if (! $synchronize) { | ||||
|         push @z,  | ||||
|             # TODO: why we have this? | ||||
|             # Vojtech: To detect the bottom interface layers by finding a Z value in the $top_z. | ||||
|         @$top_z, | ||||
|             @$top_z; | ||||
|         push @z, | ||||
|             # Top surfaces of the bottom interface layers. | ||||
|             (map $_ + $contact_distance, @$top_z); | ||||
|     } | ||||
|     @z = sort { $a <=> $b } @z; | ||||
|      | ||||
|     # enforce first layer height | ||||
|     my $first_layer_height = $self->object_config->get_value('first_layer_height'); | ||||
|  | @ -423,6 +429,11 @@ sub support_layers_z { | |||
|             1..($self->object_config->raft_layers - 2); | ||||
|     } | ||||
|      | ||||
|     if ($synchronize) { | ||||
|         @z = splice @z, $self->object_config->raft_layers; | ||||
| #            if ($self->object_config->raft_layers > scalar(@z)); | ||||
|         push @z, map $_->print_z, @{$object->layers}; | ||||
|     } else { | ||||
|         # create other layers (skip raft layers as they're already done and use thicker layers) | ||||
|         for (my $i = $#z; $i >= $self->object_config->raft_layers; $i--) { | ||||
|             my $target_height = $support_material_height; | ||||
|  | @ -442,6 +453,7 @@ sub support_layers_z { | |||
|                 $i++; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     # remove duplicates and make sure all 0.x values have the leading 0 | ||||
|     { | ||||
|  | @ -584,9 +596,8 @@ sub generate_base_layers { | |||
|                         $fillet_radius_scaled,  | ||||
|                         -$fillet_radius_scaled, | ||||
|                         # Use a geometric offsetting for filleting. | ||||
|                         CLIPPER_OFFSET_SCALE, | ||||
|                         JT_ROUND, | ||||
|                         0.2*$fillet_radius_scaled*CLIPPER_OFFSET_SCALE), | ||||
|                         0.2*$fillet_radius_scaled), | ||||
|                     $trim_polygons, | ||||
|                     0); # don't apply the safety offset. | ||||
|             } | ||||
|  | @ -613,10 +624,11 @@ sub clip_with_object { | |||
|         # $layer->slices contains the full shape of layer, thus including | ||||
|         # perimeter's width. $support contains the full shape of support | ||||
|         # material, thus including the width of its foremost extrusion. | ||||
|         # We leave a gap equal to a full extrusion width. | ||||
|         # We leave a gap equal to a full extrusion width + an offset | ||||
|         # if the user wants to play around with this setting. | ||||
|         $support->{$i} = diff( | ||||
|             $support->{$i}, | ||||
|             offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow->scaled_width), | ||||
|             offset([ map @$_, map @{$_->slices}, @layers ], +$self->object_config->support_material_xy_spacing), | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  | @ -628,8 +640,8 @@ sub generate_toolpaths { | |||
|     my $interface_flow  = $self->interface_flow; | ||||
|      | ||||
|     # shape of contact area | ||||
|     my $contact_loops   = 1; | ||||
|     my $circle_radius   = 1.5 * $interface_flow->scaled_width; | ||||
|     my $contact_loops   = $self->object_config->support_material_interface_contact_loops ? 1 : 0; | ||||
|     my $circle_radius   = 1.5 * $interface_flow->scaled_width - ($self->object_config->support_material_xy_spacing / 0.000001) ; | ||||
|     my $circle_distance = 3 * $circle_radius; | ||||
|     my $circle          = Slic3r::Polygon->new(map [ $circle_radius * cos $_, $circle_radius * sin $_ ], | ||||
|                             (5*PI/3, 4*PI/3, PI, 2*PI/3, PI/3, 0)); | ||||
|  | @ -692,11 +704,14 @@ sub generate_toolpaths { | |||
|             # if no interface layers were requested we treat the contact layer | ||||
|             # exactly as a generic base layer | ||||
|             push @$base, @$contact; | ||||
|         } elsif (@$contact && $contact_loops > 0) { | ||||
|         } elsif ($contact_loops == 0) { | ||||
|             # No contact loops, but some interface layers. Print the contact layer as a normal interface layer. | ||||
|             push @$interface, @$contact; | ||||
|         } elsif (@$contact) { | ||||
|             # generate the outermost loop | ||||
|              | ||||
|             # find centerline of the external loop (or any other kind of extrusions should the loop be skipped) | ||||
|             $contact = offset($contact, -$_interface_flow->scaled_width/2); | ||||
|             $contact = offset($contact, -($_interface_flow->scaled_width + ($self->object_config->support_material_xy_spacing / 0.000001)) /2); | ||||
|              | ||||
|             my @loops0 = (); | ||||
|             { | ||||
|  |  | |||
|  | @ -426,6 +426,9 @@ $j | |||
|                         Support material angle in degrees (range: 0-90, default: $config->{support_material_angle}) | ||||
|     --support-material-contact-distance | ||||
|                         Vertical distance between object and support material (0+, default: $config->{support_material_contact_distance}) | ||||
|     --support-material-xy-spacing | ||||
|                         "XY separation between an object and its support. If expressed as percentage (for example 50%), | ||||
|                         it will be calculated over external perimeter width (default: half of exteral perimeter width) | ||||
|     --support-material-interface-layers | ||||
|                         Number of perpendicular layers between support material and object (0+, default: $config->{support_material_interface_layers}) | ||||
|     --support-material-interface-spacing | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ use Slic3r::Test; | |||
|             interface_flow      => $flow, | ||||
|             first_layer_flow    => $flow, | ||||
|         ); | ||||
|         my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height); | ||||
|         my $support_z = $support->support_layers_z($print->print->objects->[0], \@contact_z, \@top_z, $config->layer_height); | ||||
|         my $expected_top_spacing = $support->contact_distance($config->layer_height, $config->nozzle_diameter->[0]); | ||||
|          | ||||
|         is $support_z->[0], $config->first_layer_height, | ||||
|  |  | |||
							
								
								
									
										19
									
								
								xs/Build.PL
									
										
									
									
									
								
							
							
						
						
									
										19
									
								
								xs/Build.PL
									
										
									
									
									
								
							|  | @ -117,17 +117,21 @@ if (defined $ENV{BOOST_LIBRARYDIR}) { | |||
| 
 | ||||
| # In order to generate the -l switches we need to know how Boost libraries are named | ||||
| my $have_boost = 0; | ||||
| my @boost_libraries = qw(system thread);  # we need these | ||||
| my @boost_libraries = qw(system thread log);  # we need these | ||||
| 
 | ||||
| # check without explicit lib path (works on Linux) | ||||
| if (! $mswin) { | ||||
| if (!$ENV{SLIC3R_STATIC}) { | ||||
|     # Dynamic linking of boost libraries. | ||||
|     push @cflags, qw(-DBOOST_LOG_DYN_LINK); | ||||
|     if (! $mswin) { | ||||
|         # Check without explicit lib path (works on Linux and OSX). | ||||
|         $have_boost = 1 | ||||
|             if check_lib( | ||||
|                 lib     => [ map "boost_${_}", @boost_libraries ], | ||||
|             ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| if (!$ENV{SLIC3R_STATIC} && $have_boost) { | ||||
| if ($have_boost) { | ||||
|     # The boost library was detected by check_lib on Linux. | ||||
|     push @LIBS, map "-lboost_${_}", @boost_libraries; | ||||
| } else { | ||||
|  | @ -138,8 +142,7 @@ if (!$ENV{SLIC3R_STATIC} && $have_boost) { | |||
|         # Try to find the boost system library. | ||||
|         my @files = glob "$path/${lib_prefix}system*$lib_ext"; | ||||
|         next if !@files; | ||||
|      | ||||
|         if ($files[0] =~ /${lib_prefix}system([^.]+)$lib_ext$/) { | ||||
|         if ($files[0] =~ /\Q${lib_prefix}system\E([^.]*)\Q$lib_ext\E$/) { | ||||
|             # Suffix contains the version number, the build type etc. | ||||
|             my $suffix = $1; | ||||
|             # Verify existence of all required boost libraries at $path. | ||||
|  | @ -212,6 +215,10 @@ if ($cpp_guess->is_gcc) { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| print "\n"; | ||||
| print 'With @INC: ', join(', ', map "\"$_\"", @INC), "\n"; | ||||
| print 'With @LIBS: ', join(', ', map "\"$_\"", @LIBS), "\n"; | ||||
| 
 | ||||
| my $build = Module::Build::WithXSpp->new( | ||||
|     module_name     => 'Slic3r::XS', | ||||
|     dist_abstract   => 'XS code for Slic3r', | ||||
|  |  | |||
							
								
								
									
										10
									
								
								xs/MANIFEST
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								xs/MANIFEST
									
										
									
									
									
								
							|  | @ -32,6 +32,8 @@ src/libslic3r/ExtrusionEntityCollection.cpp | |||
| src/libslic3r/ExtrusionEntityCollection.hpp | ||||
| src/libslic3r/ExtrusionSimulator.cpp | ||||
| src/libslic3r/ExtrusionSimulator.hpp | ||||
| src/libslic3r/Fill/Fill.cpp | ||||
| src/libslic3r/Fill/Fill.hpp | ||||
| src/libslic3r/Fill/FillBase.cpp | ||||
| src/libslic3r/Fill/FillBase.hpp | ||||
| src/libslic3r/Fill/FillConcentric.cpp | ||||
|  | @ -54,6 +56,8 @@ src/libslic3r/GCodeSender.cpp | |||
| src/libslic3r/GCodeSender.hpp | ||||
| src/libslic3r/GCodeWriter.cpp | ||||
| src/libslic3r/GCodeWriter.hpp | ||||
| src/libslic3r/GCode/Analyzer.cpp | ||||
| src/libslic3r/GCode/Analyzer.hpp | ||||
| src/libslic3r/GCode/PressureEqualizer.cpp | ||||
| src/libslic3r/GCode/PressureEqualizer.hpp | ||||
| src/libslic3r/Geometry.cpp | ||||
|  | @ -88,6 +92,10 @@ src/libslic3r/PrintConfig.cpp | |||
| src/libslic3r/PrintConfig.hpp | ||||
| src/libslic3r/PrintObject.cpp | ||||
| src/libslic3r/PrintRegion.cpp | ||||
| src/libslic3r/Slicing.cpp | ||||
| src/libslic3r/Slicing.hpp | ||||
| src/libslic3r/SlicingAdaptive.cpp | ||||
| src/libslic3r/SlicingAdaptive.hpp | ||||
| src/libslic3r/SupportMaterial.cpp | ||||
| src/libslic3r/SupportMaterial.hpp | ||||
| src/libslic3r/Surface.cpp | ||||
|  | @ -99,6 +107,7 @@ src/libslic3r/SVG.hpp | |||
| src/libslic3r/TriangleMesh.cpp | ||||
| src/libslic3r/TriangleMesh.hpp | ||||
| src/libslic3r/utils.cpp | ||||
| src/libslic3r/Utils.hpp | ||||
| src/perlglue.cpp | ||||
| src/poly2tri/common/shapes.cc | ||||
| src/poly2tri/common/shapes.h | ||||
|  | @ -200,7 +209,6 @@ xsp/Polygon.xsp | |||
| xsp/Polyline.xsp | ||||
| xsp/PolylineCollection.xsp | ||||
| xsp/Print.xsp | ||||
| xsp/SupportMaterial.xsp | ||||
| xsp/Surface.xsp | ||||
| xsp/SurfaceCollection.xsp | ||||
| xsp/TriangleMesh.xsp | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ | |||
| #include <stdio.h> | ||||
| #include <stdint.h> | ||||
| #include <stddef.h> | ||||
| #include <boost/predef/detail/endian_compat.h> | ||||
| #include <boost/detail/endian.hpp> | ||||
| 
 | ||||
| #ifndef BOOST_LITTLE_ENDIAN | ||||
| #error "admesh works correctly on little endian machines only!" | ||||
|  |  | |||
|  | @ -43,23 +43,8 @@ stl_open(stl_file *stl, char *file) { | |||
| 
 | ||||
| void | ||||
| stl_initialize(stl_file *stl) { | ||||
|   stl->error = 0; | ||||
|   stl->stats.degenerate_facets = 0; | ||||
|   stl->stats.edges_fixed  = 0; | ||||
|   stl->stats.facets_added = 0; | ||||
|   stl->stats.facets_removed = 0; | ||||
|   stl->stats.facets_reversed = 0; | ||||
|   stl->stats.normals_fixed = 0; | ||||
|   stl->stats.number_of_parts = 0; | ||||
|   stl->stats.original_num_facets = 0; | ||||
|   stl->stats.number_of_facets = 0; | ||||
|   stl->stats.facets_malloced = 0; | ||||
|   memset(stl, 0, sizeof(stl_file)); | ||||
|   stl->stats.volume = -1.0; | ||||
| 
 | ||||
|   stl->neighbors_start = NULL; | ||||
|   stl->facet_start = NULL; | ||||
|   stl->v_indices = NULL; | ||||
|   stl->v_shared = NULL; | ||||
| } | ||||
| 
 | ||||
| void | ||||
|  | @ -270,6 +255,7 @@ stl_read(stl_file *stl, int first_facet, int first) { | |||
|     rewind(stl->fp); | ||||
|   } | ||||
| 
 | ||||
|   char normal_buf[3][32]; | ||||
|   for(i = first_facet; i < stl->stats.number_of_facets; i++) { | ||||
|     if(stl->stats.type == binary) | ||||
|       /* Read a single facet from a binary .STL file */ | ||||
|  | @ -287,17 +273,25 @@ stl_read(stl_file *stl, int first_facet, int first) { | |||
|       fscanf(stl->fp, "endsolid\n"); | ||||
|       fscanf(stl->fp, "solid%*[^\n]\n");  // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
 | ||||
|        | ||||
|       if((fscanf(stl->fp, " facet normal %f %f %f\n", &facet.normal.x, &facet.normal.y, &facet.normal.z) + \ | ||||
|           fscanf(stl->fp, " outer loop\n") + \ | ||||
|           fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y,  &facet.vertex[0].z) + \ | ||||
|           fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y,  &facet.vertex[1].z) + \ | ||||
|           fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y,  &facet.vertex[2].z) + \ | ||||
|           fscanf(stl->fp, " endloop\n") + \ | ||||
|       if((fscanf(stl->fp, " facet normal %31s %31s %31s\n", normal_buf[0], normal_buf[1], normal_buf[2]) + | ||||
|           fscanf(stl->fp, " outer loop\n") + | ||||
|           fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y,  &facet.vertex[0].z) + | ||||
|           fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y,  &facet.vertex[1].z) + | ||||
|           fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y,  &facet.vertex[2].z) + | ||||
|           fscanf(stl->fp, " endloop\n") + | ||||
|           fscanf(stl->fp, " endfacet\n")) != 12) { | ||||
|         perror("Something is syntactically very wrong with this ASCII STL!"); | ||||
|         stl->error = 1; | ||||
|         return; | ||||
|       } | ||||
|       // The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
 | ||||
| 	  if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 || | ||||
| 		  sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 || | ||||
| 		  sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) { | ||||
| 		  // Normal was mangled. Maybe denormals or "not a number" were stored?
 | ||||
| 		  // Just reset the normal and silently ignore it.
 | ||||
| 		  memset(&facet.normal, 0, sizeof(facet.normal)); | ||||
| 	  } | ||||
|     } | ||||
| 
 | ||||
| #if 0 | ||||
|  |  | |||
|  | @ -154,16 +154,34 @@ private: | |||
|     void AddChild(PolyNode& child); | ||||
|     friend class Clipper; //to access Index
 | ||||
|     friend class ClipperOffset; | ||||
|     friend class PolyTree; //to implement the PolyTree::move operator
 | ||||
| }; | ||||
| 
 | ||||
| class PolyTree: public PolyNode | ||||
| {  | ||||
| public: | ||||
|     ~PolyTree(){Clear();}; | ||||
|     PolyTree() {} | ||||
|     PolyTree(PolyTree &&src) { *this = std::move(src); } | ||||
|     virtual ~PolyTree(){Clear();}; | ||||
|     PolyTree& operator=(PolyTree &&src) {  | ||||
|         AllNodes   = std::move(src.AllNodes); | ||||
|         Contour    = std::move(src.Contour); | ||||
|         Childs     = std::move(src.Childs); | ||||
|         Parent     = nullptr; | ||||
|         Index      = src.Index; | ||||
|         m_IsOpen   = src.m_IsOpen; | ||||
|         m_jointype = src.m_jointype; | ||||
|         m_endtype  = src.m_endtype; | ||||
|         for (size_t i = 0; i < Childs.size(); ++ i) | ||||
|           Childs[i]->Parent = this; | ||||
|         return *this;  | ||||
|     } | ||||
|     PolyNode* GetFirst() const; | ||||
|     void Clear(); | ||||
|     int Total() const; | ||||
| private: | ||||
|     PolyTree(const PolyTree &src) = delete; | ||||
|     PolyTree& operator=(const PolyTree &src) = delete; | ||||
|     PolyNodes AllNodes; | ||||
|     friend class Clipper; //to access AllNodes
 | ||||
| }; | ||||
|  |  | |||
|  | @ -277,4 +277,26 @@ BoundingBoxBase<PointClass>::overlap(const BoundingBoxBase<PointClass> &other) c | |||
| template bool BoundingBoxBase<Point>::overlap(const BoundingBoxBase<Point> &point) const; | ||||
| template bool BoundingBoxBase<Pointf>::overlap(const BoundingBoxBase<Pointf> &point) const; | ||||
| 
 | ||||
| 
 | ||||
| // Align a coordinate to a grid. The coordinate may be negative,
 | ||||
| // the aligned value will never be bigger than the original one.
 | ||||
| static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) { | ||||
|     // Current C++ standard defines the result of integer division to be rounded to zero,
 | ||||
|     // for both positive and negative numbers. Here we want to round down for negative
 | ||||
|     // numbers as well.
 | ||||
|     coord_t aligned = (coord < 0) ? | ||||
|             ((coord - spacing + 1) / spacing) * spacing : | ||||
|             (coord / spacing) * spacing; | ||||
|     assert(aligned <= coord); | ||||
|     return aligned; | ||||
| } | ||||
| 
 | ||||
| void BoundingBox::align_to_grid(const coord_t cell_size) | ||||
| { | ||||
|     if (this->defined) { | ||||
|         min.x = _align_to_grid(min.x, cell_size); | ||||
|         min.y = _align_to_grid(min.y, cell_size); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -65,6 +65,9 @@ class BoundingBox : public BoundingBoxBase<Point> | |||
|     BoundingBox rotated(double angle, const Point ¢er) const; | ||||
|     void rotate(double angle) { (*this) = this->rotated(angle); } | ||||
|     void rotate(double angle, const Point ¢er) { (*this) = this->rotated(angle, center); } | ||||
|     // Align the min corner to a grid of cell_size x cell_size cells,
 | ||||
|     // to encompass the original bounding box.
 | ||||
|     void align_to_grid(const coord_t cell_size); | ||||
|      | ||||
|     BoundingBox() : BoundingBoxBase<Point>() {}; | ||||
|     BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {}; | ||||
|  |  | |||
|  | @ -40,13 +40,13 @@ void BridgeDetector::initialize() | |||
|     this->angle = -1.; | ||||
| 
 | ||||
|     // Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
 | ||||
|     Polygons grown = offset(this->expolygons, float(this->spacing)); | ||||
|     Polygons grown = offset(to_polygons(this->expolygons), float(this->spacing)); | ||||
|      | ||||
|     // Detect possible anchoring edges of this bridging region.
 | ||||
|     // Detect what edges lie on lower slices by turning bridge contour and holes
 | ||||
|     // into polylines and then clipping them with each lower slice's contour.
 | ||||
|     // Currently _edges are only used to set a candidate direction of the bridge (see bridge_direction_candidates()).
 | ||||
|     intersection(to_polylines(grown), this->lower_slices.contours(), &this->_edges); | ||||
|     this->_edges = intersection_pl(to_polylines(grown), this->lower_slices.contours()); | ||||
|      | ||||
|     #ifdef SLIC3R_DEBUG | ||||
|     printf("  bridge has " PRINTF_ZU " support(s)\n", this->_edges.size()); | ||||
|  | @ -117,7 +117,7 @@ BridgeDetector::detect_angle() | |||
|         double total_length = 0; | ||||
|         double max_length = 0; | ||||
|         { | ||||
|             Lines clipped_lines = intersection(lines, clip_area); | ||||
|             Lines clipped_lines = intersection_ln(lines, clip_area); | ||||
|             for (size_t i = 0; i < clipped_lines.size(); ++i) { | ||||
|                 const Line &line = clipped_lines[i]; | ||||
|                 if (expolygons_contain(this->_anchor_regions, line.a) && expolygons_contain(this->_anchor_regions, line.b)) { | ||||
|  | @ -203,17 +203,19 @@ std::vector<double> BridgeDetector::bridge_direction_candidates() const | |||
|     return angles; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| BridgeDetector::coverage(double angle, Polygons* coverage) const | ||||
| Polygons BridgeDetector::coverage(double angle) const | ||||
| { | ||||
|     if (angle == -1) angle = this->angle; | ||||
|     if (angle == -1) return; | ||||
|     if (angle == -1) | ||||
|         angle = this->angle; | ||||
| 
 | ||||
|     Polygons covered; | ||||
| 
 | ||||
|     if (angle != -1) { | ||||
| 
 | ||||
|         // Get anchors, convert them to Polygons and rotate them.
 | ||||
|         Polygons anchors = to_polygons(this->_anchor_regions); | ||||
|         polygons_rotate(anchors, PI/2.0 - angle); | ||||
|          | ||||
|     Polygons covered; | ||||
|         for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) | ||||
|         { | ||||
|             // Clone our expolygon and rotate it so that we work with vertical lines.
 | ||||
|  | @ -231,7 +233,7 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const | |||
|                 it->get_trapezoids2(&trapezoids, PI/2.0); | ||||
|              | ||||
|             for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) { | ||||
|             Lines supported = intersection(trapezoid->lines(), anchors); | ||||
|                 Lines supported = intersection_ln(trapezoid->lines(), anchors); | ||||
|                 size_t n_supported = 0; | ||||
|                 // not nice, we need a more robust non-numeric check
 | ||||
|                 for (size_t i = 0; i < supported.size(); ++i) | ||||
|  | @ -248,7 +250,7 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const | |||
| 
 | ||||
|         // Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
 | ||||
|         polygons_rotate(covered, -(PI/2.0 - angle)); | ||||
|     intersection(covered, to_polygons(this->expolygons), coverage); | ||||
|     	covered = intersection(covered, to_polygons(this->expolygons)); | ||||
| 
 | ||||
|         /*
 | ||||
|         if (0) { | ||||
|  | @ -265,14 +267,8 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const | |||
|             ); | ||||
|         } | ||||
|         */ | ||||
| } | ||||
| 
 | ||||
| Polygons | ||||
| BridgeDetector::coverage(double angle) const | ||||
| { | ||||
|     Polygons pp; | ||||
|     this->coverage(angle, &pp); | ||||
|     return pp; | ||||
|     } | ||||
|     return covered; | ||||
| } | ||||
| 
 | ||||
| /*  This method returns the bridge edges (as polylines) that are not supported
 | ||||
|  | @ -288,9 +284,7 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const | |||
| 
 | ||||
|     for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) {     | ||||
|         // get unsupported bridge edges (both contour and holes)
 | ||||
|         Polylines unuspported_polylines; | ||||
|         diff(to_polylines(*it_expoly), grown_lower, &unuspported_polylines); | ||||
|         Lines unsupported_lines = to_lines(unuspported_polylines); | ||||
|         Lines unsupported_lines = to_lines(diff_pl(to_polylines(*it_expoly), grown_lower)); | ||||
|         /*  Split into individual segments and filter out edges parallel to the bridging angle
 | ||||
|             TODO: angle tolerance should probably be based on segment length and flow width, | ||||
|             so that we build supports whenever there's a chance that at least one or two bridge | ||||
|  |  | |||
|  | @ -32,7 +32,6 @@ public: | |||
|     BridgeDetector(ExPolygon _expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); | ||||
|     BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); | ||||
|     bool detect_angle(); | ||||
|     void coverage(double angle, Polygons* coverage) const; | ||||
|     Polygons coverage(double angle = -1) const; | ||||
|     void unsupported_edges(double angle, Polylines* unsupported) const; | ||||
|     Polylines unsupported_edges(double angle = -1) const; | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -14,154 +14,202 @@ using ClipperLib::jtSquare; | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| // Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library.
 | ||||
| //FIXME Vojtech: Better to use a power of 2 coefficient and to use bit shifts for scaling.
 | ||||
| // How about 2^17=131072?
 | ||||
| // By the way, is the scalling needed at all? Cura runs all the computation with a fixed point precision of 1um, while Slic3r scales to 1nm,
 | ||||
| // further scaling by 10e5 brings us to 
 | ||||
| #define CLIPPER_OFFSET_SCALE 100000.0 | ||||
| 
 | ||||
| //-----------------------------------------------------------
 | ||||
| // legacy code from Clipper documentation
 | ||||
| void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons); | ||||
| void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons); | ||||
| //-----------------------------------------------------------
 | ||||
| 
 | ||||
| void Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output); | ||||
| template <class T> | ||||
| void Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output); | ||||
| template <class T> | ||||
| void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output); | ||||
| template <class T> | ||||
| void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output); | ||||
| void ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output); | ||||
| 
 | ||||
| void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale); | ||||
| ClipperLib::Path   Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input); | ||||
| ClipperLib::Paths  Slic3rMultiPoints_to_ClipperPaths(const Polygons &input); | ||||
| ClipperLib::Paths  Slic3rMultiPoints_to_ClipperPaths(const Polylines &input); | ||||
| Slic3r::Polygon    ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input); | ||||
| Slic3r::Polyline   ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input); | ||||
| Slic3r::Polygons   ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input); | ||||
| Slic3r::Polylines  ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input); | ||||
| Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input); | ||||
| 
 | ||||
| // offset Polygons
 | ||||
| void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     double miterLimit = 3); | ||||
| void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     double miterLimit = 3); | ||||
| Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     double miterLimit = 3); | ||||
| 
 | ||||
| // This is a safe variant of the polygon offset, tailored for a single ExPolygon:
 | ||||
| // a single polygon with multiple non-overlapping holes.
 | ||||
| // Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours.
 | ||||
| void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, | ||||
|     double miterLimit = 3); | ||||
| void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, | ||||
|     double miterLimit = 3); | ||||
| Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, | ||||
|     double miterLimit = 3); | ||||
| Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, | ||||
|     double miterLimit = 3); | ||||
| Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, | ||||
|     double scale, ClipperLib::JoinType joinType, double miterLimit); | ||||
| Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, | ||||
|     double scale, ClipperLib::JoinType joinType, double miterLimit); | ||||
| ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); | ||||
| ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); | ||||
| inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  double miterLimit = 3) | ||||
|     { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } | ||||
| inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) | ||||
|     { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } | ||||
| 
 | ||||
| // offset Polylines
 | ||||
| void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,  | ||||
|     double miterLimit = 3); | ||||
| void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,  | ||||
|     double miterLimit = 3); | ||||
| void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,  | ||||
|     double miterLimit = 3); | ||||
| inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) | ||||
|     { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } | ||||
| inline Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) | ||||
|     { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } | ||||
| 
 | ||||
| void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     double miterLimit = 3); | ||||
| Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, | ||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     double miterLimit = 3); | ||||
| // offset expolygons and surfaces
 | ||||
| ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit); | ||||
| ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit); | ||||
| inline Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) | ||||
|     { return ClipperPaths_to_Slic3rPolygons(_offset(expolygon, delta, joinType, miterLimit)); } | ||||
| inline Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) | ||||
|     { return ClipperPaths_to_Slic3rPolygons(_offset(expolygons, delta, joinType, miterLimit)); } | ||||
| inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) | ||||
|     { return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }     | ||||
| inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) | ||||
|     { return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } | ||||
| inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) | ||||
|     { return ClipperPaths_to_Slic3rExPolygons(_offset(expolygon, delta, joinType, miterLimit)); } | ||||
| inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) | ||||
|     { return ClipperPaths_to_Slic3rExPolygons(_offset(expolygons, delta, joinType, miterLimit)); } | ||||
| 
 | ||||
| void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, | ||||
|     const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     double miterLimit = 3); | ||||
| void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1, | ||||
|     const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
| ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1, | ||||
|     const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     double miterLimit = 3); | ||||
| Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1, | ||||
|     const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     double miterLimit = 3); | ||||
| void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1, | ||||
|     const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     double miterLimit = 3); | ||||
| Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1, | ||||
|     const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ||||
|     double miterLimit = 3); | ||||
| 
 | ||||
| template <class T> | ||||
| void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,  | ||||
|     const Slic3r::Polygons &clip, T* retval, bool safety_offset_); | ||||
| void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,  | ||||
|     const Slic3r::Polygons &clip, ClipperLib::Paths* retval, bool safety_offset_); | ||||
| void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,  | ||||
|     const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_); | ||||
| void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,  | ||||
|     const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); | ||||
| void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,  | ||||
|     const Slic3r::Polygons &clip, Slic3r::Polylines* retval); | ||||
| void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject,  | ||||
|     const Slic3r::Polygons &clip, Slic3r::Lines* retval); | ||||
| Slic3r::Polygons _clipper(ClipperLib::ClipType clipType, | ||||
|     const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||
| Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType, | ||||
|     const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||
| Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, | ||||
|     const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||
| Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, | ||||
|     const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||
| Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, | ||||
|     const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||
| 
 | ||||
| template <class SubjectType, class ResultType> | ||||
| void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); | ||||
| // diff
 | ||||
| inline Slic3r::Polygons | ||||
| diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper(ClipperLib::ctDifference, subject, clip, safety_offset_); | ||||
| } | ||||
| 
 | ||||
| template <class SubjectType, class ResultType> | ||||
| void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_ = false); | ||||
| inline Slic3r::ExPolygons | ||||
| diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_ex(ClipperLib::ctDifference, subject, clip, safety_offset_); | ||||
| } | ||||
| 
 | ||||
| Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||
| inline Slic3r::ExPolygons | ||||
| diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_ex(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_); | ||||
| } | ||||
| 
 | ||||
| template <class SubjectType, class ClipType> | ||||
| Slic3r::ExPolygons diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_ = false); | ||||
| inline Slic3r::Polygons | ||||
| diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_); | ||||
| } | ||||
| 
 | ||||
| template <class SubjectType, class ResultType> | ||||
| void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); | ||||
| inline Slic3r::Polylines | ||||
| diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_); | ||||
| } | ||||
| 
 | ||||
| template <class SubjectType> | ||||
| SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||
| inline Slic3r::Polylines | ||||
| diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_); | ||||
| } | ||||
| 
 | ||||
| Slic3r::ExPolygons | ||||
| intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||
| inline Slic3r::Lines | ||||
| diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_ln(ClipperLib::ctDifference, subject, clip, safety_offset_); | ||||
| } | ||||
| 
 | ||||
| template <class SubjectType> | ||||
| bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||
| // intersection
 | ||||
| inline Slic3r::Polygons | ||||
| intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper(ClipperLib::ctIntersection, subject, clip, safety_offset_); | ||||
| } | ||||
| 
 | ||||
| void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval,  | ||||
|     bool safety_offset_ = false); | ||||
| inline Slic3r::ExPolygons | ||||
| intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_ex(ClipperLib::ctIntersection, subject, clip, safety_offset_); | ||||
| } | ||||
| 
 | ||||
| template <class T> | ||||
| void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_ = false); | ||||
| inline Slic3r::ExPolygons | ||||
| intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_ex(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_); | ||||
| } | ||||
| 
 | ||||
| Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset = false); | ||||
| Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset = false); | ||||
| Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_offset = false); | ||||
| inline Slic3r::Polygons | ||||
| intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_); | ||||
| } | ||||
| 
 | ||||
| void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset = false); | ||||
| Slic3r::Polygons union_(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset = false); | ||||
| inline Slic3r::Polylines | ||||
| intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_); | ||||
| } | ||||
| 
 | ||||
| void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_ = false); | ||||
| void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_ = false); | ||||
| static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval); | ||||
| inline Slic3r::Polylines | ||||
| intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_); | ||||
| } | ||||
| 
 | ||||
| void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear = false); | ||||
| void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear = false); | ||||
| inline Slic3r::Lines | ||||
| intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_ln(ClipperLib::ctIntersection, subject, clip, safety_offset_); | ||||
| } | ||||
| 
 | ||||
| // union
 | ||||
| inline Slic3r::Polygons | ||||
| union_(const Slic3r::Polygons &subject, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_); | ||||
| } | ||||
| 
 | ||||
| inline Slic3r::Polygons | ||||
| union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper(ClipperLib::ctUnion, subject, subject2, safety_offset_); | ||||
| } | ||||
| 
 | ||||
| inline Slic3r::ExPolygons | ||||
| union_ex(const Slic3r::Polygons &subject, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_ex(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_); | ||||
| } | ||||
| 
 | ||||
| inline Slic3r::ExPolygons | ||||
| union_ex(const Slic3r::ExPolygons &subject, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_); | ||||
| } | ||||
| 
 | ||||
| inline Slic3r::ExPolygons | ||||
| union_ex(const Slic3r::Surfaces &subject, bool safety_offset_ = false) | ||||
| { | ||||
|     return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false); | ||||
| Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false); | ||||
| void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval); | ||||
| 
 | ||||
| /* OTHER */ | ||||
| Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); | ||||
| Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false); | ||||
| 
 | ||||
| void safety_offset(ClipperLib::Paths* paths); | ||||
| 
 | ||||
| Polygons top_level_islands(const Slic3r::Polygons &polygons); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -1,6 +1,7 @@ | |||
| #ifndef slic3r_Config_hpp_ | ||||
| #define slic3r_Config_hpp_ | ||||
| 
 | ||||
| #include <assert.h> | ||||
| #include <map> | ||||
| #include <climits> | ||||
| #include <cstdio> | ||||
|  | @ -73,11 +74,8 @@ class ConfigOptionVector : public ConfigOptionVectorBase | |||
|     }; | ||||
|      | ||||
|     T get_at(size_t i) const { | ||||
|         try { | ||||
|             return this->values.at(i); | ||||
|         } catch (const std::out_of_range& oor) { | ||||
|             return this->values.front(); | ||||
|         } | ||||
|         assert(! this->values.empty()); | ||||
|         return (i < this->values.size()) ? this->values[i] : this->values.front(); | ||||
|     }; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| #include <algorithm> | ||||
| #include <vector> | ||||
| #include <float.h> | ||||
| #include <unordered_map> | ||||
| 
 | ||||
| #ifdef SLIC3R_GUI | ||||
| #include <wx/image.h> | ||||
|  | @ -109,7 +110,6 @@ void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resol | |||
| void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) | ||||
| { | ||||
| 	// 1) Measure the bounding box.
 | ||||
| 	m_bbox.defined = false; | ||||
| 	for (size_t i = 0; i < m_contours.size(); ++ i) { | ||||
| 		const Slic3r::Points &pts = *m_contours[i]; | ||||
| 		for (size_t j = 0; j < pts.size(); ++ j) | ||||
|  | @ -839,6 +839,8 @@ void EdgeGrid::Grid::calculate_sdf() | |||
| 	} | ||||
| 
 | ||||
| #if 0 | ||||
| 	static int iRun = 0; | ||||
| 	++ iRun; | ||||
| //#ifdef SLIC3R_GUI
 | ||||
| 	{  | ||||
| 		wxImage img(ncols, nrows); | ||||
|  | @ -862,7 +864,7 @@ void EdgeGrid::Grid::calculate_sdf() | |||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		img.SaveFile(debug_out_path("unsigned_df.png"), wxBITMAP_TYPE_PNG); | ||||
| 		img.SaveFile(debug_out_path("unsigned_df-%d.png", iRun), wxBITMAP_TYPE_PNG); | ||||
| 	} | ||||
| 	{ | ||||
| 		wxImage img(ncols, nrows); | ||||
|  | @ -895,7 +897,7 @@ void EdgeGrid::Grid::calculate_sdf() | |||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		img.SaveFile(debug_out_path("signed_df.png"), wxBITMAP_TYPE_PNG); | ||||
| 		img.SaveFile(debug_out_path("signed_df-%d.png", iRun), wxBITMAP_TYPE_PNG); | ||||
| 	} | ||||
| #endif /* SLIC3R_GUI */ | ||||
| 
 | ||||
|  | @ -1020,7 +1022,7 @@ void EdgeGrid::Grid::calculate_sdf() | |||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		img.SaveFile(debug_out_path("signed_df-signs.png"), wxBITMAP_TYPE_PNG); | ||||
| 		img.SaveFile(debug_out_path("signed_df-signs-%d.png", iRun), wxBITMAP_TYPE_PNG); | ||||
| 	} | ||||
| #endif /* SLIC3R_GUI */ | ||||
| 
 | ||||
|  | @ -1049,7 +1051,7 @@ void EdgeGrid::Grid::calculate_sdf() | |||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		img.SaveFile(debug_out_path("signed_df2.png"), wxBITMAP_TYPE_PNG); | ||||
| 		img.SaveFile(debug_out_path("signed_df2-%d.png", iRun), wxBITMAP_TYPE_PNG); | ||||
| 	} | ||||
| #endif /* SLIC3R_GUI */ | ||||
| } | ||||
|  | @ -1222,6 +1224,135 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo | |||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const | ||||
| { | ||||
| 	typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType; | ||||
| 	// 0) Prepare a binary grid.
 | ||||
| 	size_t cell_rows = m_rows + 2; | ||||
| 	size_t cell_cols = m_cols + 2; | ||||
| 	std::vector<char> cell_inside(cell_rows * cell_cols, false); | ||||
| 	for (int r = 0; r < int(cell_rows); ++ r) | ||||
| 		for (int c = 0; c < int(cell_cols); ++ c) | ||||
| 			cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1); | ||||
| 	// Fill in empty cells, which have a left / right neighbor filled.
 | ||||
| 	// Fill in empty cells, which have the top / bottom neighbor filled.
 | ||||
| 	{ | ||||
| 		std::vector<char> cell_inside2(cell_inside); | ||||
| 		for (int r = 1; r + 1 < int(cell_rows); ++ r) { | ||||
| 			for (int c = 1; c + 1 < int(cell_cols); ++ c) { | ||||
| 				int addr = r * cell_cols + c; | ||||
| 				if ((cell_inside2[addr - 1] && cell_inside2[addr + 1]) || | ||||
| 					(cell_inside2[addr - cell_cols] && cell_inside2[addr + cell_cols])) | ||||
| 					cell_inside[addr] = true; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// 1) Collect the lines.
 | ||||
| 	std::vector<Line> lines; | ||||
| 	EndPointMapType start_point_to_line_idx; | ||||
| 	for (int r = 0; r <= int(m_rows); ++ r) { | ||||
| 		for (int c = 0; c <= int(m_cols); ++ c) { | ||||
| 			int  addr    = (r + 1) * cell_cols + c + 1; | ||||
| 			bool left    = cell_inside[addr - 1]; | ||||
| 			bool top     = cell_inside[addr - cell_cols]; | ||||
| 			bool current = cell_inside[addr]; | ||||
| 			if (left != current) { | ||||
| 				lines.push_back( | ||||
| 					left ?  | ||||
| 						Line(Point(c, r+1), Point(c, r  )) :  | ||||
| 						Line(Point(c, r  ), Point(c, r+1))); | ||||
| 				start_point_to_line_idx.insert(std::pair<Point, int>(lines.back().a, int(lines.size()) - 1)); | ||||
| 			} | ||||
| 			if (top != current) { | ||||
| 				lines.push_back( | ||||
| 					top ?  | ||||
| 						Line(Point(c  , r), Point(c+1, r)) : | ||||
| 						Line(Point(c+1, r), Point(c  , r)));  | ||||
| 				start_point_to_line_idx.insert(std::pair<Point, int>(lines.back().a, int(lines.size()) - 1)); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// 2) Chain the lines.
 | ||||
| 	std::vector<char> line_processed(lines.size(), false); | ||||
| 	Polygons out; | ||||
| 	for (int i_candidate = 0; i_candidate < int(lines.size()); ++ i_candidate) { | ||||
| 		if (line_processed[i_candidate]) | ||||
| 			continue; | ||||
| 		Polygon poly; | ||||
| 		line_processed[i_candidate] = true; | ||||
| 		poly.points.push_back(lines[i_candidate].b); | ||||
| 		int i_line_current = i_candidate; | ||||
| 		for (;;) { | ||||
| 			std::pair<EndPointMapType::iterator,EndPointMapType::iterator> line_range =  | ||||
| 				start_point_to_line_idx.equal_range(lines[i_line_current].b); | ||||
| 			// The interval has to be non empty, there shall be at least one line continuing the current one.
 | ||||
| 			assert(line_range.first != line_range.second); | ||||
| 			int i_next = -1; | ||||
| 			for (EndPointMapType::iterator it = line_range.first; it != line_range.second; ++ it) { | ||||
| 				if (it->second == i_candidate) { | ||||
| 					// closing the loop.
 | ||||
| 					goto end_of_poly; | ||||
| 				} | ||||
| 				if (line_processed[it->second]) | ||||
| 					continue; | ||||
| 				if (i_next == -1) { | ||||
| 					i_next = it->second; | ||||
| 				} else { | ||||
| 					// This is a corner, where two lines meet exactly. Pick the line, which encloses a smallest angle with
 | ||||
| 					// the current edge.
 | ||||
| 					const Line &line_current = lines[i_line_current]; | ||||
| 					const Line &line_next = lines[it->second]; | ||||
| 					const Vector v1 = line_current.vector(); | ||||
| 					const Vector v2 = line_next.vector(); | ||||
| 					int64_t cross = int64_t(v1.x) * int64_t(v2.y) - int64_t(v2.x) * int64_t(v1.y); | ||||
| 					if (cross > 0) { | ||||
| 						// This has to be a convex right angle. There is no better next line.
 | ||||
| 						i_next = it->second; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			line_processed[i_next] = true; | ||||
| 			i_line_current = i_next; | ||||
| 			poly.points.push_back(lines[i_line_current].b); | ||||
| 		} | ||||
| 	end_of_poly: | ||||
| 		out.push_back(std::move(poly)); | ||||
| 	} | ||||
| 
 | ||||
| 	// 3) Scale the polygons back into world, shrink slightly and remove collinear points.
 | ||||
| 	for (size_t i = 0; i < out.size(); ++ i) { | ||||
| 		Polygon &poly = out[i]; | ||||
| 		for (size_t j = 0; j < poly.points.size(); ++ j) { | ||||
| 			Point &p = poly.points[j]; | ||||
| 			p.x *= m_resolution; | ||||
| 			p.y *= m_resolution; | ||||
| 			p.x += m_bbox.min.x; | ||||
| 			p.y += m_bbox.min.y; | ||||
| 		} | ||||
| 		// Shrink the contour slightly, so if the same contour gets discretized and simplified again, one will get the same result.
 | ||||
| 		// Remove collineaer points.
 | ||||
| 		Points pts; | ||||
| 		pts.reserve(poly.points.size()); | ||||
| 		for (size_t j = 0; j < poly.points.size(); ++ j) { | ||||
| 			size_t j0 = (j == 0) ? poly.points.size() - 1 : j - 1; | ||||
| 			size_t j2 = (j + 1 == poly.points.size()) ? 0 : j + 1; | ||||
| 			Point  v  = poly.points[j2] - poly.points[j0]; | ||||
| 			if (v.x != 0 && v.y != 0) { | ||||
| 				// This is a corner point. Copy it to the output contour.
 | ||||
| 				Point p = poly.points[j]; | ||||
| 				p.y += (v.x < 0) ? - offset : offset; | ||||
| 				p.x += (v.y > 0) ? - offset : offset; | ||||
| 				pts.push_back(p); | ||||
| 			}  | ||||
| 		} | ||||
| 		poly.points = std::move(pts); | ||||
| 	} | ||||
| 	return out; | ||||
| } | ||||
| 
 | ||||
| #ifdef SLIC3R_GUI | ||||
| void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path) | ||||
| { | ||||
|  | @ -1235,17 +1366,18 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo | |||
| 	++iRun; | ||||
|      | ||||
|     const coord_t search_radius = grid.resolution() * 2; | ||||
| 	const coord_t display_blend_radius = grid.resolution() * 5; | ||||
| 	const coord_t display_blend_radius = grid.resolution() * 2; | ||||
| 	for (coord_t r = 0; r < h; ++r) { | ||||
|     	for (coord_t c = 0; c < w; ++ c) { | ||||
| 			unsigned char *pxl = data + (((h - r - 1) * w) + c) * 3; | ||||
| 			Point pt(c * resolution + bbox.min.x, r * resolution + bbox.min.y); | ||||
| 			coordf_t min_dist; | ||||
| 			bool on_segment; | ||||
| //			if (grid.signed_distance_edges(pt, search_radius, min_dist, &on_segment)) {
 | ||||
| 			bool on_segment = true; | ||||
| 			#if 0 | ||||
| 			if (grid.signed_distance_edges(pt, search_radius, min_dist, &on_segment)) { | ||||
| 			#else | ||||
| 			if (grid.signed_distance(pt, search_radius, min_dist)) { | ||||
| 				//FIXME
 | ||||
| 				on_segment = true; | ||||
| 			#endif | ||||
| 				float s = 255 * std::abs(min_dist) / float(display_blend_radius); | ||||
| 				int is = std::max(0, std::min(255, int(floor(s + 0.5f)))); | ||||
| 				if (min_dist < 0) { | ||||
|  | @ -1254,8 +1386,8 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo | |||
| 						pxl[1] = 255 - is; | ||||
| 						pxl[2] = 255 - is; | ||||
| 					} else { | ||||
| 						pxl[0] = 128; | ||||
| 						pxl[1] = 128; | ||||
| 						pxl[0] = 255; | ||||
| 						pxl[1] = 0; | ||||
| 						pxl[2] = 255 - is; | ||||
| 					} | ||||
| 				} | ||||
|  |  | |||
|  | @ -12,11 +12,14 @@ | |||
| namespace Slic3r { | ||||
| namespace EdgeGrid { | ||||
| 
 | ||||
| struct Grid | ||||
| class Grid | ||||
| { | ||||
| public: | ||||
| 	Grid(); | ||||
| 	~Grid(); | ||||
| 
 | ||||
| 	void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; } | ||||
| 
 | ||||
| 	void create(const Polygons &polygons, coord_t resolution); | ||||
| 	void create(const ExPolygon &expoly, coord_t resolution); | ||||
| 	void create(const ExPolygons &expolygons, coord_t resolution); | ||||
|  | @ -54,6 +57,9 @@ struct Grid | |||
| 	const size_t		rows() const { return m_rows; } | ||||
| 	const size_t		cols() const { return m_cols; } | ||||
| 
 | ||||
| 	// For supports: Contours enclosing the rasterized edges.
 | ||||
| 	Polygons 			contours_simplified(coord_t offset) const; | ||||
| 
 | ||||
| protected: | ||||
| 	struct Cell { | ||||
| 		Cell() : begin(0), end(0) {} | ||||
|  | @ -65,6 +71,18 @@ protected: | |||
| #if 0 | ||||
| 	bool line_cell_intersect(const Point &p1, const Point &p2, const Cell &cell); | ||||
| #endif | ||||
| 	bool cell_inside_or_crossing(int r, int c) const | ||||
| 	{ | ||||
| 		if (r < 0 || r >= m_rows || | ||||
| 			c < 0 || c >= m_cols) | ||||
| 			// The cell is outside the domain. Hoping that the contours were correctly oriented, so
 | ||||
| 			// there is a CCW outmost contour so the out of domain cells are outside.
 | ||||
| 			return false; | ||||
| 		const Cell &cell = m_cells[r * m_cols + c]; | ||||
| 		return  | ||||
| 			(cell.begin < cell.end) ||  | ||||
| 			(! m_signed_distance_field.empty() && m_signed_distance_field[r * (m_cols + 1) + c] <= 0.f); | ||||
| 	} | ||||
| 
 | ||||
| 	// Bounding box around the contours.
 | ||||
| 	BoundingBox 								m_bbox; | ||||
|  |  | |||
|  | @ -99,9 +99,7 @@ ExPolygon::contains(const Line &line) const | |||
| bool | ||||
| ExPolygon::contains(const Polyline &polyline) const | ||||
| { | ||||
|     Polylines pl_out; | ||||
|     diff((Polylines)polyline, *this, &pl_out); | ||||
|     return pl_out.empty(); | ||||
|     return diff_pl((Polylines)polyline, *this).empty(); | ||||
| } | ||||
| 
 | ||||
| bool | ||||
|  | @ -115,8 +113,7 @@ ExPolygon::contains(const Polylines &polylines) const | |||
|     svg.draw_outline(*this); | ||||
|     svg.draw(polylines, "blue"); | ||||
|     #endif | ||||
|     Polylines pl_out; | ||||
|     diff(polylines, *this, &pl_out); | ||||
|     Polylines pl_out = diff_pl(polylines, *this); | ||||
|     #if 0 | ||||
|     svg.draw(pl_out, "red"); | ||||
|     #endif | ||||
|  | @ -162,8 +159,7 @@ ExPolygon::overlaps(const ExPolygon &other) const | |||
|     svg.draw_outline(*this); | ||||
|     svg.draw_outline(other, "blue"); | ||||
|     #endif | ||||
|     Polylines pl_out; | ||||
|     intersection((Polylines)other, *this, &pl_out); | ||||
|     Polylines pl_out = intersection_pl((Polylines)other, *this); | ||||
|     #if 0 | ||||
|     svg.draw(pl_out, "red"); | ||||
|     #endif | ||||
|  | @ -396,11 +392,8 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const | |||
|         poly[3].y = bb.max.y; | ||||
|          | ||||
|         // intersect with this expolygon
 | ||||
|         Polygons trapezoids; | ||||
|         intersection<Polygons,Polygons>(poly, *this, &trapezoids); | ||||
|          | ||||
|         // append results to return value
 | ||||
|         polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end()); | ||||
|         polygons_append(*polygons, intersection(poly, to_polygons(*this))); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -434,16 +427,13 @@ ExPolygon::triangulate_pp(Polygons* polygons) const | |||
|     // convert polygons
 | ||||
|     std::list<TPPLPoly> input; | ||||
|      | ||||
|     Polygons pp = *this; | ||||
|     simplify_polygons(pp, &pp, true); | ||||
|     ExPolygons expp; | ||||
|     union_(pp, &expp); | ||||
|     ExPolygons expp = union_ex(simplify_polygons(to_polygons(*this), true)); | ||||
|      | ||||
|     for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { | ||||
|         // contour
 | ||||
|         { | ||||
|             TPPLPoly p; | ||||
|             p.Init(ex->contour.points.size()); | ||||
|             p.Init(int(ex->contour.points.size())); | ||||
|             //printf(PRINTF_ZU "\n0\n", ex->contour.points.size());
 | ||||
|             for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) { | ||||
|                 p[ point-ex->contour.points.begin() ].x = point->x; | ||||
|  | @ -480,8 +470,8 @@ ExPolygon::triangulate_pp(Polygons* polygons) const | |||
|         Polygon p; | ||||
|         p.points.resize(num_points); | ||||
|         for (long i = 0; i < num_points; ++i) { | ||||
|             p.points[i].x = (*poly)[i].x; | ||||
|             p.points[i].y = (*poly)[i].y; | ||||
|             p.points[i].x = coord_t((*poly)[i].x); | ||||
|             p.points[i].y = coord_t((*poly)[i].y); | ||||
|         } | ||||
|         polygons->push_back(p); | ||||
|     } | ||||
|  | @ -490,8 +480,7 @@ ExPolygon::triangulate_pp(Polygons* polygons) const | |||
| void | ||||
| ExPolygon::triangulate_p2t(Polygons* polygons) const | ||||
| { | ||||
|     ExPolygons expp; | ||||
|     simplify_polygons(*this, &expp, true); | ||||
|     ExPolygons expp = simplify_polygons_ex(*this, true); | ||||
|      | ||||
|     for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { | ||||
|         // TODO: prevent duplicate points
 | ||||
|  |  | |||
|  | @ -13,9 +13,17 @@ typedef std::vector<ExPolygon> ExPolygons; | |||
| 
 | ||||
| class ExPolygon | ||||
| { | ||||
|     public: | ||||
| public: | ||||
|     ExPolygon() {} | ||||
|     ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {} | ||||
|     ExPolygon(ExPolygon &&other) : contour(std::move(other.contour)), holes(std::move(other.holes)) {} | ||||
| 
 | ||||
|     ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; } | ||||
|     ExPolygon& operator=(ExPolygon &&other) { contour = std::move(other.contour); holes = std::move(other.holes); return *this; } | ||||
| 
 | ||||
|     Polygon contour; | ||||
|     Polygons holes; | ||||
| 
 | ||||
|     operator Points() const; | ||||
|     operator Polygons() const; | ||||
|     operator Polylines() const; | ||||
|  | @ -253,6 +261,18 @@ inline void polygons_append(Polygons &dst, ExPolygons &&src) | |||
| } | ||||
| #endif | ||||
| 
 | ||||
| inline void expolygons_append(ExPolygons &dst, const ExPolygons &src)  | ||||
| {  | ||||
|     dst.insert(dst.end(), src.begin(), src.end()); | ||||
| } | ||||
| 
 | ||||
| #if SLIC3R_CPPVER >= 11 | ||||
| inline void expolygons_append(ExPolygons &dst, ExPolygons &&src) | ||||
| {  | ||||
|     std::move(std::begin(src), std::end(src), std::back_inserter(dst)); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| inline void expolygons_rotate(ExPolygons &expolys, double angle) | ||||
| { | ||||
|     for (ExPolygons::iterator p = expolys.begin(); p != expolys.end(); ++p) | ||||
|  | @ -281,37 +301,37 @@ extern bool        remove_sticks(ExPolygon &poly); | |||
| #include <boost/polygon/polygon.hpp> | ||||
| namespace boost { namespace polygon { | ||||
|     template <> | ||||
|         struct polygon_traits<ExPolygon> { | ||||
|         struct polygon_traits<Slic3r::ExPolygon> { | ||||
|         typedef coord_t coordinate_type; | ||||
|         typedef Points::const_iterator iterator_type; | ||||
|         typedef Point point_type; | ||||
|         typedef Slic3r::Points::const_iterator iterator_type; | ||||
|         typedef Slic3r::Point point_type; | ||||
| 
 | ||||
|         // Get the begin iterator
 | ||||
|         static inline iterator_type begin_points(const ExPolygon& t) { | ||||
|         static inline iterator_type begin_points(const Slic3r::ExPolygon& t) { | ||||
|             return t.contour.points.begin(); | ||||
|         } | ||||
| 
 | ||||
|         // Get the end iterator
 | ||||
|         static inline iterator_type end_points(const ExPolygon& t) { | ||||
|         static inline iterator_type end_points(const Slic3r::ExPolygon& t) { | ||||
|             return t.contour.points.end(); | ||||
|         } | ||||
| 
 | ||||
|         // Get the number of sides of the polygon
 | ||||
|         static inline std::size_t size(const ExPolygon& t) { | ||||
|         static inline std::size_t size(const Slic3r::ExPolygon& t) { | ||||
|             return t.contour.points.size(); | ||||
|         } | ||||
| 
 | ||||
|         // Get the winding direction of the polygon
 | ||||
|         static inline winding_direction winding(const ExPolygon& t) { | ||||
|         static inline winding_direction winding(const Slic3r::ExPolygon& t) { | ||||
|             return unknown_winding; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     template <> | ||||
|     struct polygon_mutable_traits<ExPolygon> { | ||||
|     struct polygon_mutable_traits<Slic3r::ExPolygon> { | ||||
|         //expects stl style iterators
 | ||||
|         template <typename iT> | ||||
|         static inline ExPolygon& set_points(ExPolygon& expolygon, iT input_begin, iT input_end) { | ||||
|         static inline Slic3r::ExPolygon& set_points(Slic3r::ExPolygon& expolygon, iT input_begin, iT input_end) { | ||||
|             expolygon.contour.points.assign(input_begin, input_end); | ||||
|             // skip last point since Boost will set last point = first point
 | ||||
|             expolygon.contour.points.pop_back(); | ||||
|  | @ -321,27 +341,27 @@ namespace boost { namespace polygon { | |||
|      | ||||
|      | ||||
|     template <> | ||||
|     struct geometry_concept<ExPolygon> { typedef polygon_with_holes_concept type; }; | ||||
|     struct geometry_concept<Slic3r::ExPolygon> { typedef polygon_with_holes_concept type; }; | ||||
| 
 | ||||
|     template <> | ||||
|     struct polygon_with_holes_traits<ExPolygon> { | ||||
|         typedef Polygons::const_iterator iterator_holes_type; | ||||
|         typedef Polygon hole_type; | ||||
|         static inline iterator_holes_type begin_holes(const ExPolygon& t) { | ||||
|     struct polygon_with_holes_traits<Slic3r::ExPolygon> { | ||||
|         typedef Slic3r::Polygons::const_iterator iterator_holes_type; | ||||
|         typedef Slic3r::Polygon hole_type; | ||||
|         static inline iterator_holes_type begin_holes(const Slic3r::ExPolygon& t) { | ||||
|             return t.holes.begin(); | ||||
|         } | ||||
|         static inline iterator_holes_type end_holes(const ExPolygon& t) { | ||||
|         static inline iterator_holes_type end_holes(const Slic3r::ExPolygon& t) { | ||||
|             return t.holes.end(); | ||||
|         } | ||||
|         static inline unsigned int size_holes(const ExPolygon& t) { | ||||
|         static inline unsigned int size_holes(const Slic3r::ExPolygon& t) { | ||||
|             return (int)t.holes.size(); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     template <> | ||||
|     struct polygon_with_holes_mutable_traits<ExPolygon> { | ||||
|     struct polygon_with_holes_mutable_traits<Slic3r::ExPolygon> { | ||||
|          template <typename iT> | ||||
|          static inline ExPolygon& set_holes(ExPolygon& t, iT inputBegin, iT inputEnd) { | ||||
|          static inline Slic3r::ExPolygon& set_holes(Slic3r::ExPolygon& t, iT inputBegin, iT inputEnd) { | ||||
|               t.holes.assign(inputBegin, inputEnd); | ||||
|               return t; | ||||
|          } | ||||
|  | @ -349,32 +369,32 @@ namespace boost { namespace polygon { | |||
|      | ||||
|     //first we register CPolygonSet as a polygon set
 | ||||
|     template <> | ||||
|     struct geometry_concept<ExPolygons> { typedef polygon_set_concept type; }; | ||||
|     struct geometry_concept<Slic3r::ExPolygons> { typedef polygon_set_concept type; }; | ||||
| 
 | ||||
|     //next we map to the concept through traits
 | ||||
|     template <> | ||||
|     struct polygon_set_traits<ExPolygons> { | ||||
|     struct polygon_set_traits<Slic3r::ExPolygons> { | ||||
|         typedef coord_t coordinate_type; | ||||
|         typedef ExPolygons::const_iterator iterator_type; | ||||
|         typedef ExPolygons operator_arg_type; | ||||
|         typedef Slic3r::ExPolygons::const_iterator iterator_type; | ||||
|         typedef Slic3r::ExPolygons operator_arg_type; | ||||
| 
 | ||||
|         static inline iterator_type begin(const ExPolygons& polygon_set) { | ||||
|         static inline iterator_type begin(const Slic3r::ExPolygons& polygon_set) { | ||||
|             return polygon_set.begin(); | ||||
|         } | ||||
| 
 | ||||
|         static inline iterator_type end(const ExPolygons& polygon_set) { | ||||
|         static inline iterator_type end(const Slic3r::ExPolygons& polygon_set) { | ||||
|             return polygon_set.end(); | ||||
|         } | ||||
| 
 | ||||
|         //don't worry about these, just return false from them
 | ||||
|         static inline bool clean(const ExPolygons& polygon_set) { return false; } | ||||
|         static inline bool sorted(const ExPolygons& polygon_set) { return false; } | ||||
|         static inline bool clean(const Slic3r::ExPolygons& polygon_set) { return false; } | ||||
|         static inline bool sorted(const Slic3r::ExPolygons& polygon_set) { return false; } | ||||
|     }; | ||||
| 
 | ||||
|     template <> | ||||
|     struct polygon_set_mutable_traits<ExPolygons> { | ||||
|     struct polygon_set_mutable_traits<Slic3r::ExPolygons> { | ||||
|         template <typename input_iterator_type> | ||||
|         static inline void set(ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) { | ||||
|         static inline void set(Slic3r::ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) { | ||||
|             expolygons.assign(input_begin, input_end); | ||||
|         } | ||||
|     }; | ||||
|  |  | |||
|  | @ -13,19 +13,13 @@ namespace Slic3r { | |||
| void | ||||
| ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const | ||||
| { | ||||
|     // perform clipping
 | ||||
|     Polylines clipped; | ||||
|     intersection<Polylines,Polylines>(this->polyline, collection, &clipped); | ||||
|     return this->_inflate_collection(clipped, retval); | ||||
|     this->_inflate_collection(intersection_pl(this->polyline, collection), retval); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const | ||||
| { | ||||
|     // perform clipping
 | ||||
|     Polylines clipped; | ||||
|     diff<Polylines,Polylines>(this->polyline, collection, &clipped); | ||||
|     return this->_inflate_collection(clipped, retval); | ||||
|     this->_inflate_collection(diff_pl(this->polyline, collection), retval); | ||||
| } | ||||
| 
 | ||||
| void | ||||
|  | @ -58,9 +52,7 @@ ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCo | |||
| 
 | ||||
| void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const | ||||
| { | ||||
|     Polygons tmp; | ||||
|     offset(this->polyline, &tmp, scale_(this->width/2) + scaled_epsilon); | ||||
|     polygons_append(out, STDMOVE(tmp)); | ||||
|     polygons_append(out, offset(this->polyline, float(scale_(this->width/2)) + scaled_epsilon)); | ||||
| } | ||||
| 
 | ||||
| void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const | ||||
|  | @ -68,9 +60,7 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale | |||
|     // Instantiating the Flow class to get the line spacing.
 | ||||
|     // Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler.
 | ||||
|     Flow flow(this->width, this->height, 0.f, this->is_bridge()); | ||||
|     Polygons tmp; | ||||
|     offset(this->polyline, &tmp, 0.5f * flow.scaled_spacing() + scaled_epsilon); | ||||
|     polygons_append(out, STDMOVE(tmp)); | ||||
|     polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); | ||||
| } | ||||
| 
 | ||||
| bool | ||||
|  |  | |||
|  | @ -69,11 +69,11 @@ class ExtrusionPath : public ExtrusionEntity | |||
| public: | ||||
|     Polyline polyline; | ||||
|     ExtrusionRole role; | ||||
|     // Volumetric velocity. mm^3 of plastic per mm of linear head motion
 | ||||
|     // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator.
 | ||||
|     double mm3_per_mm; | ||||
|     // Width of the extrusion.
 | ||||
|     // Width of the extrusion, used for visualization purposes.
 | ||||
|     float width; | ||||
|     // Height of the extrusion.
 | ||||
|     // Height of the extrusion, used for visualization purposed.
 | ||||
|     float height; | ||||
|      | ||||
|     ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {}; | ||||
|  | @ -194,6 +194,48 @@ class ExtrusionLoop : public ExtrusionEntity | |||
|     Polyline as_polyline() const { return this->polygon().split_at_first_point(); } | ||||
| }; | ||||
| 
 | ||||
| inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) | ||||
| { | ||||
|     dst.reserve(dst.size() + polylines.size()); | ||||
|     for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { | ||||
|         dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height)); | ||||
|         dst.back().polyline = *it_polyline; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #if SLIC3R_CPPVER >= 11 | ||||
| inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) | ||||
| { | ||||
|     dst.reserve(dst.size() + polylines.size()); | ||||
|     for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { | ||||
|         dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height)); | ||||
|         dst.back().polyline = std::move(*it_polyline); | ||||
|     } | ||||
| } | ||||
| #endif // SLIC3R_CPPVER >= 11
 | ||||
| 
 | ||||
| inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) | ||||
| { | ||||
|     dst.reserve(dst.size() + polylines.size()); | ||||
|     for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { | ||||
|         ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height); | ||||
|         dst.push_back(extrusion_path); | ||||
|         extrusion_path->polyline = *it_polyline; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #if SLIC3R_CPPVER >= 11 | ||||
| inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) | ||||
| { | ||||
|     dst.reserve(dst.size() + polylines.size()); | ||||
|     for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { | ||||
|         ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height); | ||||
|         dst.push_back(extrusion_path); | ||||
|         extrusion_path->polyline = std::move(*it_polyline); | ||||
|     } | ||||
| } | ||||
| #endif // SLIC3R_CPPVER >= 11
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -803,7 +803,7 @@ void gcode_spread_points( | |||
| 				const Cell &cell = cells[i]; | ||||
| 				acc[cell.idx.y()][cell.idx.x()] = (1.f - cell.fraction_covered) * cell.volume + cell.fraction_covered * cell.area * height_avg; | ||||
| 			} | ||||
| 		} else if (simulationType == ExtrusionSimulationSpreadExcess) { | ||||
| 		} else if (simulationType == Slic3r::ExtrusionSimulationSpreadExcess) { | ||||
| 			// The volume under the circle does not fit.
 | ||||
| 			// 1) Fill the underfilled cells and remove them from the list.
 | ||||
| 			float volume_borrowed_total = 0.; | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ enum ExtrusionSimulationType | |||
|     ExtrusionSimulationDontSpread, | ||||
|     ExtrisopmSimulationSpreadNotOverfilled, | ||||
|     ExtrusionSimulationSpreadFull, | ||||
|     ExtrusionSimulationSpreadExcess, | ||||
|     ExtrusionSimulationSpreadExcess | ||||
| }; | ||||
| 
 | ||||
| // An opaque class, to keep the boost stuff away from the header.
 | ||||
|  |  | |||
|  | @ -246,20 +246,19 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) | |||
|             flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow()); | ||||
|         } | ||||
| 
 | ||||
|         // save into layer
 | ||||
|         { | ||||
|             ExtrusionRole role = is_bridge ? erBridgeInfill : | ||||
|                 (surface.is_solid() ? ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) : erInternalInfill); | ||||
|             ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection()); | ||||
|             out.entities.push_back(&collection); | ||||
|         // Save into layer.
 | ||||
|         auto *eec = new ExtrusionEntityCollection(); | ||||
|         out.entities.push_back(eec); | ||||
|         // Only concentric fills are not sorted.
 | ||||
|             collection.no_sort = f->no_sort(); | ||||
|             for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) { | ||||
|                 ExtrusionPath *path = new ExtrusionPath(role, flow.mm3_per_mm(), flow.width, flow.height); | ||||
|                 collection.entities.push_back(path); | ||||
|                 path->polyline.points.swap(it->points); | ||||
|             } | ||||
|         } | ||||
|         eec->no_sort = f->no_sort(); | ||||
|         extrusion_entities_append_paths( | ||||
|             eec->entities, STDMOVE(polylines), | ||||
|             is_bridge ? | ||||
|                 erBridgeInfill : | ||||
|                 (surface.is_solid() ? | ||||
|                     ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) : | ||||
|                     erInternalInfill), | ||||
|             flow.mm3_per_mm(), flow.width, flow.height); | ||||
|     } | ||||
| 
 | ||||
|     // add thin fill regions
 | ||||
|  |  | |||
|  | @ -168,7 +168,7 @@ void Fill3DHoneycomb::_fill_surface_single( | |||
|         it->translate(bb.min.x, bb.min.y); | ||||
| 
 | ||||
|     // clip pattern to boundaries
 | ||||
|     intersection(polylines, (Polygons)expolygon, &polylines); | ||||
|     polylines = intersection_pl(polylines, (Polygons)expolygon); | ||||
| 
 | ||||
|     // connect lines
 | ||||
|     if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
 | ||||
|  |  | |||
|  | @ -45,8 +45,7 @@ Fill* Fill::new_from_type(const std::string &type) | |||
| Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms) | ||||
| { | ||||
|     // Perform offset.
 | ||||
|     Slic3r::ExPolygons expp; | ||||
|     offset(surface->expolygon, &expp, -0.5*scale_(this->spacing)); | ||||
|     Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(-0.5*scale_(this->spacing))); | ||||
|     // Create the infills for each of the regions.
 | ||||
|     Polylines polylines_out; | ||||
|     for (size_t i = 0; i < expp.size(); ++ i) | ||||
|  |  | |||
|  | @ -33,7 +33,7 @@ void FillConcentric::_fill_surface_single( | |||
| 
 | ||||
|     // generate paths from the outermost to the innermost, to avoid
 | ||||
|     // adhesion problems of the first central tiny loops
 | ||||
|     union_pt_chained(loops, &loops, false); | ||||
|     loops = union_pt_chained(loops, false); | ||||
|      | ||||
|     // split paths using a nearest neighbor search
 | ||||
|     size_t iPathFirst = polylines_out.size(); | ||||
|  |  | |||
|  | @ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single( | |||
|             Polylines p; | ||||
|             for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it) | ||||
|                 p.push_back((Polyline)(*it)); | ||||
|             intersection(p, (Polygons)expolygon, &paths); | ||||
|             paths = intersection_pl(p, to_polygons(expolygon)); | ||||
|         } | ||||
| 
 | ||||
|         // connect paths
 | ||||
|  | @ -122,7 +122,7 @@ void FillHoneycomb::_fill_surface_single( | |||
|         } | ||||
|          | ||||
|         // clip paths again to prevent connection segments from crossing the expolygon boundaries
 | ||||
|         intersection(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)), &paths); | ||||
|         paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON))); | ||||
|         // Move the polylines to the output, avoid a deep copy.
 | ||||
|         size_t j = polylines_out.size(); | ||||
|         polylines_out.resize(j + paths.size(), Polyline()); | ||||
|  |  | |||
|  | @ -44,7 +44,7 @@ void FillPlanePath::_fill_surface_single( | |||
|                 coord_t(floor(it->x * distance_between_lines + 0.5)),  | ||||
|                 coord_t(floor(it->y * distance_between_lines + 0.5)))); | ||||
| //      intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
 | ||||
|         intersection(polylines, (Polygons)expolygon, &polylines); | ||||
|         polylines = intersection_pl(polylines, to_polygons(expolygon)); | ||||
| 
 | ||||
| /*        
 | ||||
|         if (1) { | ||||
|  |  | |||
|  | @ -63,7 +63,7 @@ void FillRectilinear::_fill_surface_single( | |||
|         pts.push_back(it->a); | ||||
|         pts.push_back(it->b); | ||||
|     } | ||||
|     Polylines polylines = intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), false); | ||||
|     Polylines polylines = intersection_pl(polylines_src, offset(to_polygons(expolygon), scale_(0.02)), false); | ||||
| 
 | ||||
|     // FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
 | ||||
|     const float INFILL_OVERLAP_OVER_SPACING = 0.3f; | ||||
|  |  | |||
|  | @ -372,11 +372,9 @@ public: | |||
|         bool sticks_removed = remove_sticks(polygons_src); | ||||
| //        if (sticks_removed) printf("Sticks removed!\n");
 | ||||
|         polygons_outer = offset(polygons_src, aoffset1, | ||||
|             CLIPPER_OFFSET_SCALE, | ||||
|             ClipperLib::jtMiter, | ||||
|             mitterLimit); | ||||
|         polygons_inner = offset(polygons_outer, aoffset2 - aoffset1, | ||||
|             CLIPPER_OFFSET_SCALE, | ||||
|             ClipperLib::jtMiter, | ||||
|             mitterLimit); | ||||
| 		// Filter out contours with zero area or small area, contours with 2 points only.
 | ||||
|  | @ -884,7 +882,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP | |||
|         Point refpt = rotate_vector.second.rotated(- rotate_vector.first); | ||||
|         // _align_to_grid will not work correctly with positive pattern_shift.
 | ||||
|         coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing; | ||||
|         refpt.x -= (pattern_shift_scaled > 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); | ||||
|         refpt.x -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); | ||||
|         bounding_box.merge(_align_to_grid( | ||||
|             bounding_box.min,  | ||||
|             Point(line_spacing, line_spacing),  | ||||
|  | @ -894,7 +892,9 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP | |||
|     // Intersect a set of euqally spaced vertical lines wiht expolygon.
 | ||||
|     // n_vlines = ceil(bbox_width / line_spacing)
 | ||||
|     size_t  n_vlines = (bounding_box.max.x - bounding_box.min.x + line_spacing - 1) / line_spacing; | ||||
|     coord_t x0 = bounding_box.min.x + (line_spacing + SCALED_EPSILON) / 2; | ||||
| 	coord_t x0 = bounding_box.min.x; | ||||
| 	if (full_infill) | ||||
| 		x0 += (line_spacing + SCALED_EPSILON) / 2; | ||||
| 
 | ||||
| #ifdef SLIC3R_DEBUG | ||||
|     static int iRun = 0; | ||||
|  |  | |||
|  | @ -40,14 +40,17 @@ Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool | |||
| /* This method returns the centerline spacing between two adjacent extrusions 
 | ||||
|    having the same extrusion width (and other properties). */ | ||||
| float | ||||
| Flow::spacing() const { | ||||
|     if (this->bridge) { | ||||
| Flow::spacing() const  | ||||
| { | ||||
| #ifdef HAS_PERIMETER_LINE_OVERLAP | ||||
|     if (this->bridge) | ||||
|         return this->width + BRIDGE_EXTRA_SPACING; | ||||
|     } | ||||
|      | ||||
|     // rectangle with semicircles at the ends
 | ||||
|     float min_flow_spacing = this->width - this->height * (1 - PI/4.0); | ||||
|     return this->width - OVERLAP_FACTOR * (this->width - min_flow_spacing); | ||||
|     return this->width - PERIMETER_LINE_OVERLAP_FACTOR * (this->width - min_flow_spacing); | ||||
| #else | ||||
|     return this->bridge ? (this->width + BRIDGE_EXTRA_SPACING) : (this->width - this->height * (1 - PI/4.0)); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* This method returns the centerline spacing between an extrusion using this
 | ||||
|  | @ -57,23 +60,17 @@ float | |||
| Flow::spacing(const Flow &other) const { | ||||
|     assert(this->height == other.height); | ||||
|     assert(this->bridge == other.bridge); | ||||
|      | ||||
|     if (this->bridge) { | ||||
|         return this->width/2 + other.width/2 + BRIDGE_EXTRA_SPACING; | ||||
|     } | ||||
|      | ||||
|     return this->spacing()/2 + other.spacing()/2; | ||||
|     return this->bridge ?  | ||||
|         0.5f * this->width + 0.5f * other.width + BRIDGE_EXTRA_SPACING : | ||||
|         0.5f * this->spacing() + 0.5f * other.spacing(); | ||||
| } | ||||
| 
 | ||||
| /* This method returns extrusion volume per head move unit. */ | ||||
| double | ||||
| Flow::mm3_per_mm() const { | ||||
|     if (this->bridge) { | ||||
|         return (this->width * this->width) * PI/4.0; | ||||
|     } | ||||
|      | ||||
|     // rectangle with semicircles at the ends
 | ||||
|     return this->width * this->height + (this->height*this->height) / 4.0 * (PI-4.0); | ||||
| double Flow::mm3_per_mm() const  | ||||
| { | ||||
|     return this->bridge ? | ||||
|         (this->width * this->width) * PI/4.0 : | ||||
|         this->width * this->height + (this->height * this->height) / 4.0 * (PI-4.0); | ||||
| } | ||||
| 
 | ||||
| /* This static method returns bridge width for a given nozzle diameter. */ | ||||
|  | @ -85,8 +82,7 @@ float Flow::_bridge_width(float nozzle_diameter, float bridge_flow_ratio) { | |||
| } | ||||
| 
 | ||||
| /* This static method returns a sane extrusion width default. */ | ||||
| float | ||||
| Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) { | ||||
| float Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) { | ||||
|     // here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate
 | ||||
|     // shape: rectangle with semicircles at the ends
 | ||||
|     float width = ((nozzle_diameter*nozzle_diameter) * PI + (height*height) * (4.0 - PI)) / (4.0 * height); | ||||
|  | @ -106,14 +102,15 @@ Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) { | |||
| } | ||||
| 
 | ||||
| /* This static method returns the extrusion width value corresponding to the supplied centerline spacing. */ | ||||
| float | ||||
| Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) { | ||||
|     if (bridge) { | ||||
|         return spacing - BRIDGE_EXTRA_SPACING; | ||||
|     } | ||||
|      | ||||
|     // rectangle with semicircles at the ends
 | ||||
|     return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0); | ||||
| float Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge)  | ||||
| { | ||||
|     return bridge ?  | ||||
|         (spacing - BRIDGE_EXTRA_SPACING) :  | ||||
| #ifdef HAS_PERIMETER_LINE_OVERLAP | ||||
|         (spacing + PERIMETER_LINE_OVERLAP_FACTOR * height * (1 - PI/4.0)); | ||||
| #else | ||||
|         (spacing + height * (1 - PI/4.0)); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -7,8 +7,14 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| // Extra spacing of bridge threads, in mm.
 | ||||
| #define BRIDGE_EXTRA_SPACING 0.05 | ||||
| #define OVERLAP_FACTOR 1.0 | ||||
| 
 | ||||
| // Overlap factor of perimeter lines. Currently no overlap.
 | ||||
| // #define HAS_OVERLAP
 | ||||
| #ifdef HAS_PERIMETER_LINE_OVERLAP | ||||
|     #define PERIMETER_LINE_OVERLAP_FACTOR 1.0 | ||||
| #endif | ||||
| 
 | ||||
| enum FlowRole { | ||||
|     frExternalPerimeter, | ||||
|  | @ -22,8 +28,16 @@ enum FlowRole { | |||
| 
 | ||||
| class Flow | ||||
| { | ||||
|     public: | ||||
|     float width, height, nozzle_diameter; | ||||
| public: | ||||
|     // Non bridging flow: Maximum width of an extrusion with semicircles at the ends.
 | ||||
|     // Bridging flow: Bridge thread diameter.
 | ||||
|     float width; | ||||
|     // Non bridging flow: Layer height.
 | ||||
|     // Bridging flow: Bridge thread diameter = layer height.
 | ||||
|     float height; | ||||
|     // Nozzle diameter is 
 | ||||
|     float nozzle_diameter; | ||||
|     // Is it a bridge?
 | ||||
|     bool  bridge; | ||||
|      | ||||
|     Flow(float _w, float _h, float _nd, bool _bridge = false) | ||||
|  |  | |||
|  | @ -315,8 +315,7 @@ GCode::change_layer(const Layer &layer) | |||
|      | ||||
|     // avoid computing islands and overhangs if they're not needed
 | ||||
|     if (this->config.avoid_crossing_perimeters) { | ||||
|         ExPolygons islands; | ||||
|         union_(layer.slices, &islands, true); | ||||
|         ExPolygons islands = union_ex(layer.slices, true); | ||||
|         this->avoid_crossing_perimeters.init_layer_mp(islands); | ||||
|     } | ||||
|      | ||||
|  |  | |||
|  | @ -105,7 +105,7 @@ Layer::make_slices() | |||
|         FOREACH_LAYERREGION(this, layerm) { | ||||
|             polygons_append(slices_p, to_polygons((*layerm)->slices)); | ||||
|         } | ||||
|         union_(slices_p, &slices); | ||||
|         slices = union_ex(slices_p); | ||||
|     } | ||||
|      | ||||
|     this->slices.expolygons.clear(); | ||||
|  | @ -132,15 +132,11 @@ Layer::merge_slices() | |||
|     if (this->regions.size() == 1) { | ||||
|         // Optimization, also more robust. Don't merge classified pieces of layerm->slices,
 | ||||
|         // but use the non-split islands of a layer. For a single region print, these shall be equal.
 | ||||
|         this->regions.front()->slices.surfaces.clear(); | ||||
|         surfaces_append(this->regions.front()->slices.surfaces, this->slices.expolygons, stInternal); | ||||
|         this->regions.front()->slices.set(this->slices.expolygons, stInternal); | ||||
|     } else { | ||||
|         FOREACH_LAYERREGION(this, layerm) { | ||||
|             ExPolygons expp; | ||||
|             // without safety offset, artifacts are generated (GH #2494)
 | ||||
|             union_(to_polygons(STDMOVE((*layerm)->slices.surfaces)), &expp, true); | ||||
|             (*layerm)->slices.surfaces.clear(); | ||||
|             surfaces_append((*layerm)->slices.surfaces, expp, stInternal); | ||||
|             (*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -223,7 +219,7 @@ Layer::make_perimeters() | |||
|                 } | ||||
|                 // merge the surfaces assigned to each group
 | ||||
|                 for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it) | ||||
|                     surfaces_append(new_slices.surfaces, union_ex(it->second, true), it->second.front()); | ||||
|                     new_slices.append(union_ex(it->second, true), it->second.front()); | ||||
|             } | ||||
|              | ||||
|             // make perimeters
 | ||||
|  | @ -236,8 +232,7 @@ Layer::make_perimeters() | |||
|                     // Separate the fill surfaces.
 | ||||
|                     ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices); | ||||
|                     (*l)->fill_expolygons = expp; | ||||
|                     (*l)->fill_surfaces.surfaces.clear(); | ||||
|                     surfaces_append((*l)->fill_surfaces.surfaces, STDMOVE(expp), fill_surfaces.surfaces.front()); | ||||
|                     (*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -318,9 +313,4 @@ SupportLayer::SupportLayer(size_t id, PrintObject *object, coordf_t height, | |||
| { | ||||
| } | ||||
| 
 | ||||
| SupportLayer::~SupportLayer() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -11,9 +11,6 @@ | |||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| typedef std::pair<coordf_t,coordf_t> t_layer_height_range; | ||||
| typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges; | ||||
| 
 | ||||
| class Layer; | ||||
| class PrintRegion; | ||||
| class PrintObject; | ||||
|  | @ -155,7 +152,7 @@ public: | |||
| protected: | ||||
|     SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, | ||||
|         coordf_t slice_z); | ||||
|     virtual ~SupportLayer(); | ||||
|     virtual ~SupportLayer() {} | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -52,8 +52,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped() | |||
|     Polygons fill_boundaries = to_polygons(this->fill_expolygons); | ||||
|     this->fill_surfaces.surfaces.clear(); | ||||
|     for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++ surface) | ||||
|         surfaces_append( | ||||
|             this->fill_surfaces.surfaces, | ||||
|         this->fill_surfaces.append( | ||||
|             intersection_ex(to_polygons(surface->expolygon), fill_boundaries), | ||||
|             surface->surface_type); | ||||
| } | ||||
|  | @ -91,9 +90,9 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* | |||
|     g.process(); | ||||
| } | ||||
| 
 | ||||
| //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 3.
 | ||||
| //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 1.5
 | ||||
| #define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare, 0. | ||||
| //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
 | ||||
| //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
 | ||||
| #define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. | ||||
| 
 | ||||
| void | ||||
| LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||
|  | @ -194,7 +193,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) | |||
|                         break; | ||||
|                     } | ||||
|                 // Grown by 3mm.
 | ||||
|                 Polygons polys = offset(bridges[i].expolygon, float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS); | ||||
|                 Polygons polys = offset(to_polygons(bridges[i].expolygon), float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS); | ||||
|                 if (idx_island == -1) { | ||||
|                     printf("Bridge did not fall into the source region!\r\n"); | ||||
|                 } else { | ||||
|  | @ -262,9 +261,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) | |||
|                 BridgeDetector bd( | ||||
|                     initial, | ||||
|                     lower_layer->slices, | ||||
|                     //FIXME parameters are not correct!
 | ||||
|                     // flow(FlowRole role, bool bridge = false, double width = -1) const;
 | ||||
|                     this->flow(frInfill, true, this->layer()->height).scaled_width() | ||||
|                     this->flow(frInfill, true).scaled_width() | ||||
|                 ); | ||||
|                 #ifdef SLIC3R_DEBUG | ||||
|                 printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id()); | ||||
|  | @ -305,8 +302,10 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) | |||
|             polygons_append(polys, STDMOVE(s1)); | ||||
|             for (size_t j = i + 1; j < top.size(); ++ j) { | ||||
|                 Surface &s2 = top[j]; | ||||
|                 if (! s2.empty() && surfaces_could_merge(s1, s2)) | ||||
|                 if (! s2.empty() && surfaces_could_merge(s1, s2)) { | ||||
|                     polygons_append(polys, STDMOVE(s2)); | ||||
|                     s2.clear(); | ||||
|                 } | ||||
|             } | ||||
|             if (s1.surface_type == stTop) | ||||
|                 // Trim the top surfaces by the bottom surfaces. This gives the priority to the bottom surfaces.
 | ||||
|  | @ -329,8 +328,10 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) | |||
|         polygons_append(polys, STDMOVE(s1)); | ||||
|         for (size_t j = i + 1; j < internal.size(); ++ j) { | ||||
|             Surface &s2 = internal[j]; | ||||
|             if (! s2.empty() && surfaces_could_merge(s1, s2)) | ||||
|             if (! s2.empty() && surfaces_could_merge(s1, s2)) { | ||||
|                 polygons_append(polys, STDMOVE(s2)); | ||||
|                 s2.clear(); | ||||
|             } | ||||
|         } | ||||
|         ExPolygons new_expolys = diff_ex(polys, new_polygons); | ||||
|         polygons_append(new_polygons, to_polygons(new_expolys)); | ||||
|  |  | |||
|  | @ -76,20 +76,20 @@ class Linef3 | |||
|     void scale(double factor); | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| // start Boost
 | ||||
| #include <boost/polygon/polygon.hpp> | ||||
| namespace boost { namespace polygon { | ||||
|     template <> | ||||
|     struct geometry_concept<Line> { typedef segment_concept type; }; | ||||
|     struct geometry_concept<Slic3r::Line> { typedef segment_concept type; }; | ||||
| 
 | ||||
|     template <> | ||||
|     struct segment_traits<Line> { | ||||
|     struct segment_traits<Slic3r::Line> { | ||||
|         typedef coord_t coordinate_type; | ||||
|         typedef Point point_type; | ||||
|         typedef Slic3r::Point point_type; | ||||
|      | ||||
|         static inline point_type get(const Line& line, direction_1d dir) { | ||||
|         static inline point_type get(const Slic3r::Line& line, direction_1d dir) { | ||||
|             return dir.to_int() ? line.b : line.a; | ||||
|         } | ||||
|     }; | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| #include "Layer.hpp" | ||||
| #include "Point.hpp" | ||||
| #include "TriangleMesh.hpp" | ||||
| #include "Slicing.hpp" | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include <utility> | ||||
|  |  | |||
|  | @ -142,7 +142,7 @@ MotionPlanner::shortest_path(const Point &from, const Point &to) | |||
|     { | ||||
|         // grow our environment slightly in order for simplify_by_visibility()
 | ||||
|         // to work best by considering moves on boundaries valid as well
 | ||||
|         ExPolygonCollection grown_env(offset_ex(env.env, +SCALED_EPSILON)); | ||||
|         ExPolygonCollection grown_env(offset_ex(env.env.expolygons, +SCALED_EPSILON)); | ||||
|          | ||||
|         if (island_idx == -1) { | ||||
|             /*  If 'from' or 'to' are not inside our env, they were connected using the 
 | ||||
|  | @ -155,12 +155,12 @@ MotionPlanner::shortest_path(const Point &from, const Point &to) | |||
|             if (!grown_env.contains(from)) { | ||||
|                 // delete second point while the line connecting first to third crosses the
 | ||||
|                 // boundaries as many times as the current first to second
 | ||||
|                 while (polyline.points.size() > 2 && intersection((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) { | ||||
|                 while (polyline.points.size() > 2 && intersection_ln((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) { | ||||
|                     polyline.points.erase(polyline.points.begin() + 1); | ||||
|                 } | ||||
|             } | ||||
|             if (!grown_env.contains(to)) { | ||||
|                 while (polyline.points.size() > 2 && intersection((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) { | ||||
|                 while (polyline.points.size() > 2 && intersection_ln((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) { | ||||
|                     polyline.points.erase(polyline.points.end() - 2); | ||||
|                 } | ||||
|             } | ||||
|  | @ -294,7 +294,7 @@ MotionPlannerEnv::nearest_env_point(const Point &from, const Point &to) const | |||
|         size_t result = from.nearest_waypoint_index(pp, to); | ||||
|          | ||||
|         // as we assume 'from' is outside env, any node will require at least one crossing
 | ||||
|         if (intersection((Lines)Line(from, pp[result]), this->island).size() > 1) { | ||||
|         if (intersection_ln((Lines)Line(from, pp[result]), this->island).size() > 1) { | ||||
|             // discard result
 | ||||
|             pp.erase(pp.begin() + result); | ||||
|         } else { | ||||
|  |  | |||
|  | @ -54,8 +54,7 @@ PerimeterGenerator::process() | |||
|     for (Surfaces::const_iterator surface = this->slices->surfaces.begin(); | ||||
|         surface != this->slices->surfaces.end(); ++surface) { | ||||
|         // detect how many perimeters must be generated for this island
 | ||||
|         signed short loop_number = this->config->perimeters + surface->extra_perimeters; | ||||
|         loop_number--;  // 0-indexed loops
 | ||||
|         const int loop_number = this->config->perimeters + surface->extra_perimeters -1;  // 0-indexed loops
 | ||||
|          | ||||
|         Polygons gaps; | ||||
|          | ||||
|  | @ -67,7 +66,7 @@ PerimeterGenerator::process() | |||
|             ThickPolylines thin_walls; | ||||
|              | ||||
|             // we loop one time more than needed in order to find gaps after the last perimeter was applied
 | ||||
|             for (signed short i = 0; i <= loop_number+1; ++i) {  // outer loop is 0
 | ||||
|             for (int i = 0; i <= loop_number+1; ++i) {  // outer loop is 0
 | ||||
|                 Polygons offsets; | ||||
|                 if (i == 0) { | ||||
|                     // the minimum thickness of a single loop is:
 | ||||
|  | @ -170,16 +169,16 @@ PerimeterGenerator::process() | |||
|             } | ||||
|              | ||||
|             // nest loops: holes first
 | ||||
|             for (signed short d = 0; d <= loop_number; ++d) { | ||||
|             for (int d = 0; d <= loop_number; ++d) { | ||||
|                 PerimeterGeneratorLoops &holes_d = holes[d]; | ||||
|                  | ||||
|                 // loop through all holes having depth == d
 | ||||
|                 for (signed short i = 0; i < holes_d.size(); ++i) { | ||||
|                 for (int i = 0; i < (int)holes_d.size(); ++i) { | ||||
|                     const PerimeterGeneratorLoop &loop = holes_d[i]; | ||||
|                      | ||||
|                     // find the hole loop that contains this one, if any
 | ||||
|                     for (signed short t = d+1; t <= loop_number; ++t) { | ||||
|                         for (signed short j = 0; j < holes[t].size(); ++j) { | ||||
|                     for (int t = d+1; t <= loop_number; ++t) { | ||||
|                         for (int j = 0; j < (int)holes[t].size(); ++j) { | ||||
|                             PerimeterGeneratorLoop &candidate_parent = holes[t][j]; | ||||
|                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) { | ||||
|                                 candidate_parent.children.push_back(loop); | ||||
|  | @ -191,8 +190,8 @@ PerimeterGenerator::process() | |||
|                     } | ||||
|                      | ||||
|                     // if no hole contains this hole, find the contour loop that contains it
 | ||||
|                     for (signed short t = loop_number; t >= 0; --t) { | ||||
|                         for (signed short j = 0; j < contours[t].size(); ++j) { | ||||
|                     for (int t = loop_number; t >= 0; --t) { | ||||
|                         for (int j = 0; j < (int)contours[t].size(); ++j) { | ||||
|                             PerimeterGeneratorLoop &candidate_parent = contours[t][j]; | ||||
|                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) { | ||||
|                                 candidate_parent.children.push_back(loop); | ||||
|  | @ -207,16 +206,16 @@ PerimeterGenerator::process() | |||
|             } | ||||
|          | ||||
|             // nest contour loops
 | ||||
|             for (signed short d = loop_number; d >= 1; --d) { | ||||
|             for (int d = loop_number; d >= 1; --d) { | ||||
|                 PerimeterGeneratorLoops &contours_d = contours[d]; | ||||
|                  | ||||
|                 // loop through all contours having depth == d
 | ||||
|                 for (signed short i = 0; i < contours_d.size(); ++i) { | ||||
|                 for (int i = 0; i < (int)contours_d.size(); ++i) { | ||||
|                     const PerimeterGeneratorLoop &loop = contours_d[i]; | ||||
|                  | ||||
|                     // find the contour loop that contains it
 | ||||
|                     for (signed short t = d-1; t >= 0; --t) { | ||||
|                         for (signed short j = 0; j < contours[t].size(); ++j) { | ||||
|                     for (int t = d-1; t >= 0; --t) { | ||||
|                         for (int j = 0; j < contours[t].size(); ++j) { | ||||
|                             PerimeterGeneratorLoop &candidate_parent = contours[t][j]; | ||||
|                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) { | ||||
|                                 candidate_parent.children.push_back(loop); | ||||
|  | @ -315,8 +314,7 @@ PerimeterGenerator::process() | |||
|             coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE); | ||||
|              | ||||
|             // append infill areas to fill_surfaces
 | ||||
|             surfaces_append( | ||||
|                 this->fill_surfaces->surfaces,  | ||||
|             this->fill_surfaces->append( | ||||
|                 offset2_ex( | ||||
|                     pp, | ||||
|                     -inset -min_perimeter_infill_spacing/2, | ||||
|  | @ -354,36 +352,24 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, | |||
|         if (this->config->overhangs && this->layer_id > 0 | ||||
|             && !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) { | ||||
|             // get non-overhang paths by intersecting this loop with the grown lower slices
 | ||||
|             { | ||||
|                 Polylines polylines; | ||||
|                 intersection((Polygons)loop->polygon, this->_lower_slices_p, &polylines); | ||||
|                  | ||||
|                 for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) { | ||||
|                     ExtrusionPath path(role); | ||||
|                     path.polyline   = *polyline; | ||||
|                     path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm           : this->_mm3_per_mm; | ||||
|                     path.width      = is_external ? this->ext_perimeter_flow.width  : this->perimeter_flow.width; | ||||
|                     path.height     = this->layer_height; | ||||
|                     paths.push_back(path); | ||||
|                 } | ||||
|             } | ||||
|             extrusion_paths_append( | ||||
|                 paths, | ||||
|                 intersection_pl(loop->polygon, this->_lower_slices_p), | ||||
|                 role, | ||||
|                 is_external ? this->_ext_mm3_per_mm           : this->_mm3_per_mm, | ||||
|                 is_external ? this->ext_perimeter_flow.width  : this->perimeter_flow.width, | ||||
|                 this->layer_height); | ||||
|              | ||||
|             // get overhang paths by checking what parts of this loop fall 
 | ||||
|             // outside the grown lower slices (thus where the distance between
 | ||||
|             // the loop centerline and original lower slices is >= half nozzle diameter
 | ||||
|             { | ||||
|                 Polylines polylines; | ||||
|                 diff((Polygons)loop->polygon, this->_lower_slices_p, &polylines); | ||||
|                  | ||||
|                 for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) { | ||||
|                     ExtrusionPath path(erOverhangPerimeter); | ||||
|                     path.polyline   = *polyline; | ||||
|                     path.mm3_per_mm = this->_mm3_per_mm_overhang; | ||||
|                     path.width      = this->overhang_flow.width; | ||||
|                     path.height     = this->overhang_flow.height; | ||||
|                     paths.push_back(path); | ||||
|                 } | ||||
|             } | ||||
|             extrusion_paths_append( | ||||
|                 paths, | ||||
|                 diff_pl(loop->polygon, this->_lower_slices_p), | ||||
|                 erOverhangPerimeter, | ||||
|                 this->_mm3_per_mm_overhang, | ||||
|                 this->overhang_flow.width, | ||||
|                 this->overhang_flow.height); | ||||
|              | ||||
|             // reapply the nearest point search for starting point
 | ||||
|             // We allow polyline reversal because Clipper may have randomly
 | ||||
|  | @ -459,7 +445,7 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo | |||
|         ExtrusionPath path(role); | ||||
|         ThickLines lines = p->thicklines(); | ||||
|          | ||||
|         for (size_t i = 0; i < lines.size(); ++i) { | ||||
|         for (int i = 0; i < (int)lines.size(); ++i) { | ||||
|             const ThickLine& line = lines[i]; | ||||
|              | ||||
|             const coordf_t line_len = line.length(); | ||||
|  |  | |||
|  | @ -12,12 +12,6 @@ Point::Point(double x, double y) | |||
|     this->y = lrint(y); | ||||
| } | ||||
| 
 | ||||
| bool | ||||
| Point::operator==(const Point& rhs) const | ||||
| { | ||||
|     return this->coincides_with(rhs); | ||||
| } | ||||
| 
 | ||||
| std::string | ||||
| Point::wkt() const | ||||
| { | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ class Point | |||
|     static Point new_scale(coordf_t x, coordf_t y) { | ||||
|         return Point(scale_(x), scale_(y)); | ||||
|     }; | ||||
|     bool operator==(const Point& rhs) const; | ||||
|     bool operator==(const Point& rhs) const { return this->x == rhs.x && this->y == rhs.y; } | ||||
|     std::string wkt() const; | ||||
|     std::string dump_perl() const; | ||||
|     void scale(double factor); | ||||
|  | @ -70,6 +70,12 @@ inline Point operator+(const Point& point1, const Point& point2) { return Point( | |||
| inline Point operator-(const Point& point1, const Point& point2) { return Point(point1.x - point2.x, point1.y - point2.y); } | ||||
| inline Point operator*(double scalar, const Point& point2) { return Point(scalar * point2.x, scalar * point2.y); } | ||||
| 
 | ||||
| struct PointHash { | ||||
|     size_t operator()(const Point &pt) const { | ||||
|         return std::hash<coord_t>()(pt.x) ^ std::hash<coord_t>()(pt.y); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| class Point3 : public Point | ||||
| { | ||||
|     public: | ||||
|  | @ -105,6 +111,9 @@ class Pointf | |||
| inline Pointf operator+(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x + point2.x, point1.y + point2.y); } | ||||
| inline Pointf operator-(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x - point2.x, point1.y - point2.y); } | ||||
| inline Pointf operator*(double scalar, const Pointf& point2) { return Pointf(scalar * point2.x, scalar * point2.y); } | ||||
| inline Pointf operator*(const Pointf& point2, double scalar) { return Pointf(scalar * point2.x, scalar * point2.y); } | ||||
| inline coordf_t cross(const Pointf &v1, const Pointf &v2) { return v1.x * v2.y - v1.y * v2.x; } | ||||
| inline coordf_t dot(const Pointf &v1, const Pointf &v2) { return v1.x * v1.y + v2.x * v2.y; } | ||||
| 
 | ||||
| class Pointf3 : public Pointf | ||||
| { | ||||
|  | @ -122,7 +131,7 @@ class Pointf3 : public Pointf | |||
|     Vectorf3 vector_to(const Pointf3 &point) const; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| // start Boost
 | ||||
| #include <boost/version.hpp> | ||||
|  | @ -146,28 +155,28 @@ namespace boost { namespace polygon { | |||
| #endif | ||||
| 
 | ||||
|     template <> | ||||
|     struct geometry_concept<Point> { typedef point_concept type; }; | ||||
|     struct geometry_concept<Slic3r::Point> { typedef point_concept type; }; | ||||
|     | ||||
|     template <> | ||||
|     struct point_traits<Point> { | ||||
|     struct point_traits<Slic3r::Point> { | ||||
|         typedef coord_t coordinate_type; | ||||
|      | ||||
|         static inline coordinate_type get(const Point& point, orientation_2d orient) { | ||||
|         static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) { | ||||
|             return (orient == HORIZONTAL) ? point.x : point.y; | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     template <> | ||||
|     struct point_mutable_traits<Point> { | ||||
|     struct point_mutable_traits<Slic3r::Point> { | ||||
|         typedef coord_t coordinate_type; | ||||
|         static inline void set(Point& point, orientation_2d orient, coord_t value) { | ||||
|         static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) { | ||||
|             if (orient == HORIZONTAL) | ||||
|                 point.x = value; | ||||
|             else | ||||
|                 point.y = value; | ||||
|         } | ||||
|         static inline Point construct(coord_t x_value, coord_t y_value) { | ||||
|             Point retval; | ||||
|         static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) { | ||||
|             Slic3r::Point retval; | ||||
|             retval.x = x_value; | ||||
|             retval.y = y_value;  | ||||
|             return retval; | ||||
|  |  | |||
|  | @ -112,9 +112,7 @@ double Polygon::area() const | |||
| bool | ||||
| Polygon::is_counter_clockwise() const | ||||
| { | ||||
|     ClipperLib::Path p; | ||||
|     Slic3rMultiPoint_to_ClipperPath(*this, &p); | ||||
|     return ClipperLib::Orientation(p); | ||||
|     return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this)); | ||||
| } | ||||
| 
 | ||||
| bool | ||||
|  | @ -190,8 +188,7 @@ Polygon::simplify(double tolerance) const | |||
|      | ||||
|     Polygons pp; | ||||
|     pp.push_back(p); | ||||
|     simplify_polygons(pp, &pp); | ||||
|     return pp; | ||||
|     return simplify_polygons(pp); | ||||
| } | ||||
| 
 | ||||
| void | ||||
|  |  | |||
|  | @ -142,43 +142,43 @@ inline Polylines to_polylines(Polygons &&polys) | |||
| #include <boost/polygon/polygon.hpp> | ||||
| namespace boost { namespace polygon { | ||||
|     template <> | ||||
|     struct geometry_concept<Polygon>{ typedef polygon_concept type; }; | ||||
|     struct geometry_concept<Slic3r::Polygon>{ typedef polygon_concept type; }; | ||||
| 
 | ||||
|     template <> | ||||
|     struct polygon_traits<Polygon> { | ||||
|     struct polygon_traits<Slic3r::Polygon> { | ||||
|         typedef coord_t coordinate_type; | ||||
|         typedef Points::const_iterator iterator_type; | ||||
|         typedef Point point_type; | ||||
|         typedef Slic3r::Points::const_iterator iterator_type; | ||||
|         typedef Slic3r::Point point_type; | ||||
| 
 | ||||
|         // Get the begin iterator
 | ||||
|         static inline iterator_type begin_points(const Polygon& t) { | ||||
|         static inline iterator_type begin_points(const Slic3r::Polygon& t) { | ||||
|             return t.points.begin(); | ||||
|         } | ||||
| 
 | ||||
|         // Get the end iterator
 | ||||
|         static inline iterator_type end_points(const Polygon& t) { | ||||
|         static inline iterator_type end_points(const Slic3r::Polygon& t) { | ||||
|             return t.points.end(); | ||||
|         } | ||||
| 
 | ||||
|         // Get the number of sides of the polygon
 | ||||
|         static inline std::size_t size(const Polygon& t) { | ||||
|         static inline std::size_t size(const Slic3r::Polygon& t) { | ||||
|             return t.points.size(); | ||||
|         } | ||||
| 
 | ||||
|         // Get the winding direction of the polygon
 | ||||
|         static inline winding_direction winding(const Polygon& t) { | ||||
|         static inline winding_direction winding(const Slic3r::Polygon& t) { | ||||
|             return unknown_winding; | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     template <> | ||||
|     struct polygon_mutable_traits<Polygon> { | ||||
|     struct polygon_mutable_traits<Slic3r::Polygon> { | ||||
|         // expects stl style iterators
 | ||||
|         template <typename iT> | ||||
|         static inline Polygon& set_points(Polygon& polygon, iT input_begin, iT input_end) { | ||||
|         static inline Slic3r::Polygon& set_points(Slic3r::Polygon& polygon, iT input_begin, iT input_end) { | ||||
|             polygon.points.clear(); | ||||
|             while (input_begin != input_end) { | ||||
|                 polygon.points.push_back(Point()); | ||||
|                 polygon.points.push_back(Slic3r::Point()); | ||||
|                 boost::polygon::assign(polygon.points.back(), *input_begin); | ||||
|                 ++input_begin; | ||||
|             } | ||||
|  | @ -189,32 +189,32 @@ namespace boost { namespace polygon { | |||
|     }; | ||||
|      | ||||
|     template <> | ||||
|     struct geometry_concept<Polygons> { typedef polygon_set_concept type; }; | ||||
|     struct geometry_concept<Slic3r::Polygons> { typedef polygon_set_concept type; }; | ||||
| 
 | ||||
|     //next we map to the concept through traits
 | ||||
|     template <> | ||||
|     struct polygon_set_traits<Polygons> { | ||||
|     struct polygon_set_traits<Slic3r::Polygons> { | ||||
|         typedef coord_t coordinate_type; | ||||
|         typedef Polygons::const_iterator iterator_type; | ||||
|         typedef Polygons operator_arg_type; | ||||
|         typedef Slic3r::Polygons::const_iterator iterator_type; | ||||
|         typedef Slic3r::Polygons operator_arg_type; | ||||
| 
 | ||||
|         static inline iterator_type begin(const Polygons& polygon_set) { | ||||
|         static inline iterator_type begin(const Slic3r::Polygons& polygon_set) { | ||||
|             return polygon_set.begin(); | ||||
|         } | ||||
| 
 | ||||
|         static inline iterator_type end(const Polygons& polygon_set) { | ||||
|         static inline iterator_type end(const Slic3r::Polygons& polygon_set) { | ||||
|             return polygon_set.end(); | ||||
|         } | ||||
| 
 | ||||
|         //don't worry about these, just return false from them
 | ||||
|         static inline bool clean(const Polygons& polygon_set) { return false; } | ||||
|         static inline bool sorted(const Polygons& polygon_set) { return false; } | ||||
|         static inline bool clean(const Slic3r::Polygons& polygon_set) { return false; } | ||||
|         static inline bool sorted(const Slic3r::Polygons& polygon_set) { return false; } | ||||
|     }; | ||||
| 
 | ||||
|     template <> | ||||
|     struct polygon_set_mutable_traits<Polygons> { | ||||
|     struct polygon_set_mutable_traits<Slic3r::Polygons> { | ||||
|         template <typename input_iterator_type> | ||||
|         static inline void set(Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) { | ||||
|         static inline void set(Slic3r::Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) { | ||||
|           polygons.assign(input_begin, input_end); | ||||
|         } | ||||
|     }; | ||||
|  |  | |||
|  | @ -133,12 +133,6 @@ Print::clear_regions() | |||
|         this->delete_region(i); | ||||
| } | ||||
| 
 | ||||
| PrintRegion* | ||||
| Print::get_region(size_t idx) | ||||
| { | ||||
|     return regions.at(idx); | ||||
| } | ||||
| 
 | ||||
| PrintRegion* | ||||
| Print::add_region() | ||||
| { | ||||
|  | @ -608,20 +602,15 @@ Print::validate() const | |||
|                 object->model_object()->instances.front()->transform_polygon(&convex_hull); | ||||
|                  | ||||
|                 // grow convex hull with the clearance margin
 | ||||
|                 { | ||||
|                     Polygons grown_hull; | ||||
|                     offset(convex_hull, &grown_hull, scale_(this->config.extruder_clearance_radius.value)/2, 1, jtRound, scale_(0.1)); | ||||
|                     convex_hull = grown_hull.front(); | ||||
|                 } | ||||
|                 convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front(); | ||||
|                  | ||||
|                 // now we check that no instance of convex_hull intersects any of the previously checked object instances
 | ||||
|                 for (Points::const_iterator copy = object->_shifted_copies.begin(); copy != object->_shifted_copies.end(); ++copy) { | ||||
|                     Polygon p = convex_hull; | ||||
|                     p.translate(*copy); | ||||
|                     if (intersects(a, p)) | ||||
|                     if (! intersection(a, p).empty()) | ||||
|                         return "Some objects are too close; your extruder will collide with them."; | ||||
|                      | ||||
|                     union_(a, p, &a); | ||||
|                     polygons_append(a, p); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
| #include "Layer.hpp" | ||||
| #include "Model.hpp" | ||||
| #include "PlaceholderParser.hpp" | ||||
| 
 | ||||
| #include "Slicing.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -79,6 +79,10 @@ public: | |||
|     PrintObjectConfig config; | ||||
|     t_layer_height_ranges layer_height_ranges; | ||||
| 
 | ||||
|     // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
 | ||||
|     // The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
 | ||||
|     std::vector<coordf_t> layer_height_profile; | ||||
|      | ||||
|     // this is set to true when LayerRegion->slices is split in top/internal/bottom
 | ||||
|     // so that next call to make_perimeters() performs a union() before computing loops
 | ||||
|     bool typed_slices; | ||||
|  | @ -137,11 +141,25 @@ public: | |||
|     bool invalidate_step(PrintObjectStep step); | ||||
|     bool invalidate_all_steps(); | ||||
| 
 | ||||
|     // Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile.
 | ||||
|     // The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces.
 | ||||
|     void update_layer_height_profile(); | ||||
| 
 | ||||
|     // Collect the slicing parameters, to be used by variable layer thickness algorithm,
 | ||||
|     // by the interactive layer height editor and by the printing process itself.
 | ||||
|     // The slicing parameters are dependent on various configuration values
 | ||||
|     // (layer height, first layer height, raft settings, print nozzle diameter etc).
 | ||||
|     SlicingParameters slicing_parameters() const; | ||||
| 
 | ||||
|     void _slice(); | ||||
|     bool has_support_material() const; | ||||
|     void detect_surfaces_type(); | ||||
|     void process_external_surfaces(); | ||||
|     void discover_vertical_shells(); | ||||
|     void bridge_over_infill(); | ||||
|     void _make_perimeters(); | ||||
|     void _infill(); | ||||
|     void _generate_support_material(); | ||||
| 
 | ||||
| private: | ||||
|     Print* _print; | ||||
|  | @ -152,6 +170,8 @@ private: | |||
|         // parameter
 | ||||
|     PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); | ||||
|     ~PrintObject() {} | ||||
| 
 | ||||
|     std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier); | ||||
| }; | ||||
| 
 | ||||
| typedef std::vector<PrintObject*> PrintObjectPtrs; | ||||
|  | @ -186,7 +206,8 @@ class Print | |||
|     bool reload_model_instances(); | ||||
| 
 | ||||
|     // methods for handling regions
 | ||||
|     PrintRegion* get_region(size_t idx); | ||||
|     PrintRegion* get_region(size_t idx) { return regions.at(idx); } | ||||
|     const PrintRegion* get_region(size_t idx) const  { return regions.at(idx); } | ||||
|     PrintRegion* add_region(); | ||||
|      | ||||
|     // methods for handling state
 | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| #include "PrintConfig.hpp" | ||||
| #include <boost/thread.hpp> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -1120,6 +1121,17 @@ PrintConfigDef::PrintConfigDef() | |||
|     def->cli = "support-material!"; | ||||
|     def->default_value = new ConfigOptionBool(false); | ||||
| 
 | ||||
|     def = this->add("support_material_xy_spacing", coFloatOrPercent); | ||||
|     def->label = "XY separation between an object and its support"; | ||||
|     def->category = "Support material"; | ||||
|     def->tooltip = "XY separation between an object and its support. If expressed as percentage (for example 50%), it will be calculated over external perimeter width."; | ||||
|     def->sidetext = "mm or %"; | ||||
|     def->cli = "support-material-xy-spacing=s"; | ||||
|     def->ratio_over = "external_perimeter_extrusion_width"; | ||||
|     def->min = 0; | ||||
|     // Default is half the external perimeter width.
 | ||||
|     def->default_value = new ConfigOptionFloatOrPercent(50, true); | ||||
| 
 | ||||
|     def = this->add("support_material_angle", coInt); | ||||
|     def->label = "Pattern angle"; | ||||
|     def->category = "Support material"; | ||||
|  | @ -1177,6 +1189,13 @@ PrintConfigDef::PrintConfigDef() | |||
|     def->cli = "support-material-extrusion-width=s"; | ||||
|     def->default_value = new ConfigOptionFloatOrPercent(0, false); | ||||
| 
 | ||||
|     def = this->add("support_material_interface_contact_loops", coBool); | ||||
|     def->label = "Interface circles"; | ||||
|     def->category = "Support material"; | ||||
|     def->tooltip = "Cover the top most interface layer with contact loops"; | ||||
|     def->cli = "support-material-interface-contact-loops!"; | ||||
|     def->default_value = new ConfigOptionBool(true); | ||||
| 
 | ||||
|     def = this->add("support_material_interface_extruder", coInt); | ||||
|     def->label = "Support material/raft interface extruder"; | ||||
|     def->category = "Extruders"; | ||||
|  | @ -1247,6 +1266,13 @@ PrintConfigDef::PrintConfigDef() | |||
|     def->min = 0; | ||||
|     def->default_value = new ConfigOptionFloat(60); | ||||
| 
 | ||||
|     def = this->add("support_material_synchronize_layers", coBool); | ||||
|     def->label = "Synchronize with object layers"; | ||||
|     def->category = "Support material"; | ||||
|     def->tooltip = "Synchronize support layers with the object print layers. This is useful with multi-material printers, where the extruder switch is expensive."; | ||||
|     def->cli = "support-material-synchronize-layers!"; | ||||
|     def->default_value = new ConfigOptionBool(false); | ||||
| 
 | ||||
|     def = this->add("support_material_threshold", coInt); | ||||
|     def->label = "Overhang threshold"; | ||||
|     def->category = "Support material"; | ||||
|  | @ -1290,8 +1316,10 @@ PrintConfigDef::PrintConfigDef() | |||
|     def->cli = "threads|j=i"; | ||||
|     def->readonly = true; | ||||
|     def->min = 1; | ||||
|     def->max = 16; | ||||
|     def->default_value = new ConfigOptionInt(2); | ||||
|     { | ||||
|         unsigned int threads = boost::thread::hardware_concurrency(); | ||||
|         def->default_value = new ConfigOptionInt(threads > 0 ? threads : 2); | ||||
|     } | ||||
|      | ||||
|     def = this->add("toolchange_gcode", coString); | ||||
|     def->label = "Tool change G-code"; | ||||
|  |  | |||
|  | @ -153,6 +153,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig | |||
|     ConfigOptionInt                 support_material_enforce_layers; | ||||
|     ConfigOptionInt                 support_material_extruder; | ||||
|     ConfigOptionFloatOrPercent      support_material_extrusion_width; | ||||
|     ConfigOptionBool                support_material_interface_contact_loops; | ||||
|     ConfigOptionInt                 support_material_interface_extruder; | ||||
|     ConfigOptionInt                 support_material_interface_layers; | ||||
|     ConfigOptionFloat               support_material_interface_spacing; | ||||
|  | @ -160,8 +161,10 @@ class PrintObjectConfig : public virtual StaticPrintConfig | |||
|     ConfigOptionEnum<SupportMaterialPattern> support_material_pattern; | ||||
|     ConfigOptionFloat               support_material_spacing; | ||||
|     ConfigOptionFloat               support_material_speed; | ||||
|     ConfigOptionBool                support_material_synchronize_layers; | ||||
|     ConfigOptionInt                 support_material_threshold; | ||||
|     ConfigOptionBool                support_material_with_sheath; | ||||
|     ConfigOptionFloatOrPercent      support_material_xy_spacing; | ||||
|     ConfigOptionFloat               xy_size_compensation; | ||||
|      | ||||
|     PrintObjectConfig(bool initialize = true) : StaticPrintConfig() { | ||||
|  | @ -185,6 +188,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig | |||
|         OPT_PTR(support_material_buildplate_only); | ||||
|         OPT_PTR(support_material_contact_distance); | ||||
|         OPT_PTR(support_material_enforce_layers); | ||||
|         OPT_PTR(support_material_interface_contact_loops); | ||||
|         OPT_PTR(support_material_extruder); | ||||
|         OPT_PTR(support_material_extrusion_width); | ||||
|         OPT_PTR(support_material_interface_extruder); | ||||
|  | @ -194,6 +198,8 @@ class PrintObjectConfig : public virtual StaticPrintConfig | |||
|         OPT_PTR(support_material_pattern); | ||||
|         OPT_PTR(support_material_spacing); | ||||
|         OPT_PTR(support_material_speed); | ||||
|         OPT_PTR(support_material_synchronize_layers); | ||||
|         OPT_PTR(support_material_xy_spacing); | ||||
|         OPT_PTR(support_material_threshold); | ||||
|         OPT_PTR(support_material_with_sheath); | ||||
|         OPT_PTR(xy_size_compensation); | ||||
|  |  | |||
|  | @ -2,10 +2,28 @@ | |||
| #include "BoundingBox.hpp" | ||||
| #include "ClipperUtils.hpp" | ||||
| #include "Geometry.hpp" | ||||
| #include "SVG.hpp" | ||||
| #include "SupportMaterial.hpp" | ||||
| 
 | ||||
| #include <boost/log/trivial.hpp> | ||||
| 
 | ||||
| #include <Shiny/Shiny.h> | ||||
| 
 | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
| #define SLIC3R_DEBUG | ||||
| #endif | ||||
| 
 | ||||
| // #define SLIC3R_DEBUG
 | ||||
| 
 | ||||
| // Make assert active if SLIC3R_DEBUG
 | ||||
| #ifdef SLIC3R_DEBUG | ||||
|     #undef NDEBUG | ||||
|     #define DEBUG | ||||
|     #define _DEBUG | ||||
|     #include "SVG.hpp" | ||||
|     #undef assert  | ||||
|     #include <cassert> | ||||
| #endif | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) | ||||
|  | @ -115,8 +133,12 @@ PrintObject::layer_count() const | |||
| void | ||||
| PrintObject::clear_layers() | ||||
| { | ||||
|     for (int i = this->layers.size()-1; i >= 0; --i) | ||||
|         this->delete_layer(i); | ||||
|     for (size_t i = 0; i < this->layers.size(); ++ i) { | ||||
|         Layer *layer = this->layers[i]; | ||||
|         layer->upper_layer = layer->lower_layer = nullptr; | ||||
|         delete layer; | ||||
|     } | ||||
|     this->layers.clear(); | ||||
| } | ||||
| 
 | ||||
| Layer* | ||||
|  | @ -144,8 +166,12 @@ PrintObject::support_layer_count() const | |||
| void | ||||
| PrintObject::clear_support_layers() | ||||
| { | ||||
|     for (int i = this->support_layers.size()-1; i >= 0; --i) | ||||
|         this->delete_support_layer(i); | ||||
|     for (size_t i = 0; i < this->support_layers.size(); ++ i) { | ||||
|         Layer *layer = this->support_layers[i]; | ||||
|         layer->upper_layer = layer->lower_layer = nullptr; | ||||
|         delete layer; | ||||
|     } | ||||
|     this->support_layers.clear(); | ||||
| } | ||||
| 
 | ||||
| SupportLayer* | ||||
|  | @ -197,12 +223,15 @@ PrintObject::invalidate_state_by_config_options(const std::vector<t_config_optio | |||
|             || *opt_key == "support_material_extruder" | ||||
|             || *opt_key == "support_material_extrusion_width" | ||||
|             || *opt_key == "support_material_interface_layers" | ||||
|             || *opt_key == "support_material_interface_contact_loops" | ||||
|             || *opt_key == "support_material_interface_extruder" | ||||
|             || *opt_key == "support_material_interface_spacing" | ||||
|             || *opt_key == "support_material_interface_speed" | ||||
|             || *opt_key == "support_material_buildplate_only" | ||||
|             || *opt_key == "support_material_pattern" | ||||
|             || *opt_key == "support_material_xy_spacing" | ||||
|             || *opt_key == "support_material_spacing" | ||||
|             || *opt_key == "support_material_synchronize_layers" | ||||
|             || *opt_key == "support_material_threshold" | ||||
|             || *opt_key == "support_material_with_sheath" | ||||
|             || *opt_key == "dont_support_bridges" | ||||
|  | @ -323,7 +352,8 @@ PrintObject::has_support_material() const | |||
| // If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins.
 | ||||
| void PrintObject::detect_surfaces_type() | ||||
| { | ||||
| //    Slic3r::debugf "Detecting solid surfaces...\n";
 | ||||
|     BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces..."; | ||||
| 
 | ||||
|     for (int idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
|         for (int idx_layer = 0; idx_layer < int(this->layer_count()); ++ idx_layer) { | ||||
|  | @ -435,7 +465,7 @@ void PrintObject::detect_surfaces_type() | |||
|             { | ||||
|                 Polygons topbottom = to_polygons(top); | ||||
|                 polygons_append(topbottom, to_polygons(bottom)); | ||||
|                 surfaces_append(layerm->slices.surfaces,  | ||||
|                 layerm->slices.append( | ||||
| #if 0 | ||||
|                     offset2_ex(diff(layerm_slices_surfaces, topbottom, true), -offset, offset), | ||||
| #else | ||||
|  | @ -444,8 +474,8 @@ void PrintObject::detect_surfaces_type() | |||
|                     stInternal); | ||||
|             } | ||||
| 
 | ||||
|             surfaces_append(layerm->slices.surfaces, STDMOVE(top)); | ||||
|             surfaces_append(layerm->slices.surfaces, STDMOVE(bottom)); | ||||
|             layerm->slices.append(STDMOVE(top)); | ||||
|             layerm->slices.append(STDMOVE(bottom)); | ||||
|              | ||||
| //            Slic3r::debugf "  layer %d has %d bottom, %d top and %d internal surfaces\n",
 | ||||
| //                $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
 | ||||
|  | @ -469,6 +499,8 @@ void PrintObject::detect_surfaces_type() | |||
| void | ||||
| PrintObject::process_external_surfaces() | ||||
| { | ||||
|     BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..."; | ||||
| 
 | ||||
|     FOREACH_REGION(this->_print, region) { | ||||
|         size_t region_id = region - this->_print->regions.begin(); | ||||
|          | ||||
|  | @ -497,6 +529,8 @@ PrintObject::discover_vertical_shells() | |||
| { | ||||
|     PROFILE_FUNC(); | ||||
| 
 | ||||
|     BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..."; | ||||
| 
 | ||||
|     const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; | ||||
| 
 | ||||
|     for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { | ||||
|  | @ -518,8 +552,19 @@ PrintObject::discover_vertical_shells() | |||
|         { | ||||
|             PROFILE_BLOCK(discover_vertical_shells_region_layer); | ||||
| 
 | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
| 			static size_t debug_idx = 0; | ||||
| 			++ debug_idx; | ||||
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | ||||
| 
 | ||||
|             Layer       *layer               = this->layers[idx_layer]; | ||||
|             LayerRegion *layerm              = layer->get_region(idx_region); | ||||
| 
 | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
|             layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial"); | ||||
|             layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-initial"); | ||||
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | ||||
| 
 | ||||
|             Flow         solid_infill_flow   = layerm->flow(frSolidInfill); | ||||
|             coord_t      infill_line_spacing = solid_infill_flow.scaled_spacing();  | ||||
|             // Find a union of perimeters below / above this surface to guarantee a minimum shell thickness.
 | ||||
|  | @ -532,16 +577,16 @@ PrintObject::discover_vertical_shells() | |||
|             if (1) | ||||
|             { | ||||
|                 PROFILE_BLOCK(discover_vertical_shells_region_layer_collect); | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
| #if 0 | ||||
| // #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
 | ||||
|                 { | ||||
|                     static size_t idx = 0; | ||||
|                     SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", idx), this->bounding_box()); | ||||
| 					Slic3r::SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", debug_idx), this->bounding_box()); | ||||
|                     for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) { | ||||
|                         if (n < 0 || n >= (int)this->layers.size()) | ||||
|                             continue; | ||||
|                         ExPolygons &expolys = this->layers[n]->perimeter_expolygons; | ||||
|                         for (size_t i = 0; i < expolys.size(); ++ i) { | ||||
|                             SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", idx, n, i), get_extents(expolys[i])); | ||||
| 							Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", debug_idx, n, i), get_extents(expolys[i])); | ||||
|                             svg.draw(expolys[i]); | ||||
|                             svg.draw_outline(expolys[i].contour, "black", scale_(0.05)); | ||||
|                             svg.draw_outline(expolys[i].holes, "blue", scale_(0.05)); | ||||
|  | @ -552,7 +597,6 @@ PrintObject::discover_vertical_shells() | |||
|                             svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05)); | ||||
|                         } | ||||
|                     } | ||||
|                     ++ idx; | ||||
|                 } | ||||
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | ||||
|                 // Reset the top / bottom inflated regions caches of entries, which are out of the moving window.
 | ||||
|  | @ -610,8 +654,7 @@ PrintObject::discover_vertical_shells() | |||
|                     } | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
|                 { | ||||
|                     static size_t idx = 0; | ||||
|                     SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", idx ++), get_extents(shell)); | ||||
| 					Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); | ||||
|                     svg.draw(shell); | ||||
|                     svg.draw_outline(shell, "black", scale_(0.05)); | ||||
|                     svg.Close();  | ||||
|  | @ -634,8 +677,7 @@ PrintObject::discover_vertical_shells() | |||
| 
 | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
|             { | ||||
|                 static size_t idx = 0; | ||||
|                 SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", idx ++), get_extents(shell)); | ||||
|                 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell)); | ||||
|                 svg.draw(shell_ex); | ||||
|                 svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); | ||||
|                 svg.Close();   | ||||
|  | @ -644,8 +686,7 @@ PrintObject::discover_vertical_shells() | |||
| 
 | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
|             { | ||||
|                 static size_t idx = 0; | ||||
|                 SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", idx ++), get_extents(shell)); | ||||
|                 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", debug_idx), get_extents(shell)); | ||||
|                 svg.draw(layerm->fill_surfaces.filter_by_type(stInternal), "yellow", 0.5); | ||||
|                 svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternal), "black", "blue", scale_(0.05)); | ||||
|                 svg.draw(shell_ex, "blue", 0.5); | ||||
|  | @ -653,8 +694,7 @@ PrintObject::discover_vertical_shells() | |||
|                 svg.Close(); | ||||
|             }  | ||||
|             { | ||||
|                 static size_t idx = 0; | ||||
|                 SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell)); | ||||
|                 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); | ||||
|                 svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); | ||||
|                 svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); | ||||
|                 svg.draw(shell_ex, "blue", 0.5); | ||||
|  | @ -662,8 +702,7 @@ PrintObject::discover_vertical_shells() | |||
|                 svg.Close(); | ||||
|             }  | ||||
|             { | ||||
|                 static size_t idx = 0; | ||||
|                 SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell)); | ||||
|                 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); | ||||
|                 svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); | ||||
|                 svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); | ||||
|                 svg.draw(shell_ex, "blue", 0.5); | ||||
|  | @ -674,7 +713,7 @@ PrintObject::discover_vertical_shells() | |||
| 
 | ||||
|             // Trim the shells region by the internal & internal void surfaces.
 | ||||
|             const SurfaceType surfaceTypesInternal[] = { stInternal, stInternalVoid, stInternalSolid }; | ||||
|             const Polygons    polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 2)); | ||||
|             const Polygons    polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 3)); | ||||
|             shell = intersection(shell, polygonsInternal, true); | ||||
|             polygons_append(shell, diff(polygonsInternal, holes)); | ||||
|             if (shell.empty()) | ||||
|  | @ -692,8 +731,7 @@ PrintObject::discover_vertical_shells() | |||
| #if 1 | ||||
|             // Intentionally inflate a bit more than how much the region has been shrunk, 
 | ||||
|             // so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
 | ||||
|             shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, | ||||
|                 CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare); | ||||
|             shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare); | ||||
|             if (shell.empty()) | ||||
|                 continue; | ||||
| #else | ||||
|  | @ -705,7 +743,7 @@ PrintObject::discover_vertical_shells() | |||
|             // get a triangle in $too_narrow; if we grow it below then the shell
 | ||||
|             // would have a different shape from the external surface and we'd still
 | ||||
|             // have the same angle, so the next shell would be grown even more and so on.
 | ||||
|             Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 5.), true); | ||||
|             Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, ClipperLib::jtMiter, 5.), true); | ||||
|             if (! too_narrow.empty()) { | ||||
|                 // grow the collapsing parts and add the extra area to  the neighbor layer 
 | ||||
|                 // as well as to our original surfaces so that we support this 
 | ||||
|  | @ -717,8 +755,7 @@ PrintObject::discover_vertical_shells() | |||
|             ExPolygons new_internal_solid = intersection_ex(polygonsInternal, shell, false); | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
|             { | ||||
|                 static size_t idx = 0; | ||||
|                 SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", idx ++), get_extents(shell_before)); | ||||
|                 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", debug_idx), get_extents(shell_before)); | ||||
|                 // Source shell.
 | ||||
|                 svg.draw(union_ex(shell_before, true)); | ||||
|                 // Shell trimmed to the internal surfaces.
 | ||||
|  | @ -743,26 +780,27 @@ PrintObject::discover_vertical_shells() | |||
| 
 | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
|             { | ||||
|                 static size_t idx = 0; | ||||
|                 SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal-%d.svg", idx), get_extents(shell), new_internal, "black", "blue", scale_(0.05)); | ||||
|                 SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_void-%d.svg", idx), get_extents(shell), new_internal_void, "black", "blue", scale_(0.05)); | ||||
|                 SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_solid-%d.svg", idx), get_extents(shell), new_internal_solid, "black", "blue", scale_(0.05)); | ||||
|                 ++ idx; | ||||
|                 SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal-%d.svg", debug_idx), get_extents(shell), new_internal, "black", "blue", scale_(0.05)); | ||||
| 				SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_void-%d.svg", debug_idx), get_extents(shell), new_internal_void, "black", "blue", scale_(0.05)); | ||||
| 				SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_solid-%d.svg", debug_idx), get_extents(shell), new_internal_solid, "black", "blue", scale_(0.05)); | ||||
|             } | ||||
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | ||||
| 
 | ||||
|             // Assign resulting internal surfaces to layer.
 | ||||
|             const SurfaceType surfaceTypesKeep[] = { stTop, stBottom, stBottomBridge }; | ||||
|             layerm->fill_surfaces.keep_types(surfaceTypesKeep, sizeof(surfaceTypesKeep)/sizeof(SurfaceType)); | ||||
|             layerm->fill_surfaces.append(stInternal     , new_internal); | ||||
|             layerm->fill_surfaces.append(stInternalVoid , new_internal_void); | ||||
|             layerm->fill_surfaces.append(stInternalSolid, new_internal_solid); | ||||
|             layerm->fill_surfaces.append(new_internal,       stInternal); | ||||
|             layerm->fill_surfaces.append(new_internal_void,  stInternalVoid); | ||||
|             layerm->fill_surfaces.append(new_internal_solid, stInternalSolid); | ||||
|         } // for each layer
 | ||||
| 
 | ||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||
|             layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells"); | ||||
|             layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells"); | ||||
| 		for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++idx_layer) { | ||||
| 			LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region); | ||||
| 			layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final"); | ||||
| 			layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final"); | ||||
| 		} | ||||
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | ||||
|         } // for each layer
 | ||||
|     } // for each region
 | ||||
| 
 | ||||
|     // Write the profiler measurements to file
 | ||||
|  | @ -775,6 +813,8 @@ PrintObject::discover_vertical_shells() | |||
| void | ||||
| PrintObject::bridge_over_infill() | ||||
| { | ||||
|     BOOST_LOG_TRIVIAL(info) << "Bridge over infill..."; | ||||
| 
 | ||||
|     FOREACH_REGION(this->_print, region) { | ||||
|         size_t region_id = region - this->_print->regions.begin(); | ||||
|          | ||||
|  | @ -846,7 +886,7 @@ PrintObject::bridge_over_infill() | |||
|             #endif | ||||
|              | ||||
|             // compute the remaning internal solid surfaces as difference
 | ||||
|             ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true); | ||||
|             ExPolygons not_to_bridge = diff_ex(internal_solid, to_polygons(to_bridge), true); | ||||
|             to_bridge = intersection_ex(to_polygons(to_bridge), internal_solid, true); | ||||
|              | ||||
|             // build the new collection of fill_surfaces
 | ||||
|  | @ -902,4 +942,332 @@ PrintObject::bridge_over_infill() | |||
|     } | ||||
| } | ||||
| 
 | ||||
| SlicingParameters PrintObject::slicing_parameters() const | ||||
| { | ||||
|     return SlicingParameters::create_from_config( | ||||
|         this->print()->config, this->config,  | ||||
|         unscale(this->size.z), this->print()->object_extruders()); | ||||
| } | ||||
| 
 | ||||
| void PrintObject::update_layer_height_profile() | ||||
| { | ||||
|     if (this->layer_height_profile.empty()) { | ||||
|         if (0) | ||||
| //        if (this->layer_height_profile.empty())
 | ||||
|             this->layer_height_profile = layer_height_profile_adaptive(this->slicing_parameters(), this->layer_height_ranges, | ||||
|                 this->model_object()->volumes); | ||||
|         else | ||||
|             this->layer_height_profile = layer_height_profile_from_ranges(this->slicing_parameters(), this->layer_height_ranges); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // 1) Decides Z positions of the layers,
 | ||||
| // 2) Initializes layers and their regions
 | ||||
| // 3) Slices the object meshes
 | ||||
| // 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
 | ||||
| // 5) Applies size compensation (offsets the slices in XY plane)
 | ||||
| // 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
 | ||||
| // Resulting expolygons of layer regions are marked as Internal.
 | ||||
| //
 | ||||
| // this should be idempotent
 | ||||
| void PrintObject::_slice() | ||||
| { | ||||
|     SlicingParameters slicing_params = this->slicing_parameters(); | ||||
| 
 | ||||
|     // 1) Initialize layers and their slice heights.
 | ||||
|     std::vector<float> slice_zs; | ||||
|     { | ||||
|         this->clear_layers(); | ||||
|         // Object layers (pairs of bottom/top Z coordinate), without the raft.
 | ||||
|         this->update_layer_height_profile(); | ||||
|         std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, this->layer_height_profile); | ||||
|         // Reserve object layers for the raft. Last layer of the raft is the contact layer.
 | ||||
|         int id = int(slicing_params.raft_layers()); | ||||
|         slice_zs.reserve(object_layers.size()); | ||||
|         Layer *prev = nullptr; | ||||
|         for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) { | ||||
|             coordf_t lo = object_layers[i_layer]; | ||||
|             coordf_t hi = object_layers[i_layer + 1]; | ||||
|             coordf_t slice_z = 0.5 * (lo + hi); | ||||
|             Layer *layer = this->add_layer(id ++, hi - lo, hi + slicing_params.object_print_z_min, slice_z); | ||||
|             slice_zs.push_back(float(slice_z)); | ||||
|             if (prev != nullptr) { | ||||
|                 prev->upper_layer = layer; | ||||
|                 layer->lower_layer = prev; | ||||
|             } | ||||
|             // Make sure all layers contain layer region objects for all regions.
 | ||||
|             for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) | ||||
|                 layer->add_region(this->print()->regions[region_id]); | ||||
|             prev = layer; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     if (this->print()->regions.size() == 1) { | ||||
|         // Optimized for a single region. Slice the single non-modifier mesh.
 | ||||
|         std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(0, slice_zs, false); | ||||
|         for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) | ||||
|             this->layers[layer_id]->regions.front()->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); | ||||
|     } else { | ||||
|         // Slice all non-modifier volumes.
 | ||||
|         for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { | ||||
|             std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false); | ||||
|             for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) | ||||
|                 this->layers[layer_id]->regions[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); | ||||
|         } | ||||
|         // Slice all modifier volumes.
 | ||||
|         for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { | ||||
|             std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true); | ||||
|             // loop through the other regions and 'steal' the slices belonging to this one
 | ||||
|             for (size_t other_region_id = 0; other_region_id < this->print()->regions.size(); ++ other_region_id) { | ||||
|                 if (region_id == other_region_id) | ||||
|                     continue; | ||||
|                 for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) { | ||||
|                     Layer       *layer = layers[layer_id]; | ||||
|                     LayerRegion *layerm = layer->regions[region_id]; | ||||
|                     LayerRegion *other_layerm = layer->regions[other_region_id]; | ||||
|                     if (layerm == nullptr || other_layerm == nullptr) | ||||
|                         continue; | ||||
|                     Polygons other_slices = to_polygons(other_layerm->slices); | ||||
|                     ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id])); | ||||
|                     if (my_parts.empty()) | ||||
|                         continue; | ||||
|                     // Remove such parts from original region.
 | ||||
|                     other_layerm->slices.set(diff_ex(other_slices, to_polygons(my_parts)), stInternal); | ||||
|                     // Append new parts to our region.
 | ||||
|                     layerm->slices.append(std::move(my_parts), stInternal); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     // remove last layer(s) if empty
 | ||||
|     while (! this->layers.empty()) { | ||||
|         const Layer *layer = this->layers.back(); | ||||
|         for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) | ||||
|             if (layer->regions[region_id] != nullptr && ! layer->regions[region_id]->slices.empty()) | ||||
|                 // Non empty layer.
 | ||||
|                 goto end; | ||||
|         this->delete_layer(int(this->layers.size()) - 1); | ||||
|     } | ||||
| end: | ||||
|     ; | ||||
| 
 | ||||
|     for (size_t layer_id = 0; layer_id < layers.size(); ++ layer_id) { | ||||
|         Layer *layer = this->layers[layer_id]; | ||||
|         // apply size compensation
 | ||||
|         if (this->config.xy_size_compensation.value != 0.) { | ||||
|             float delta = float(scale_(this->config.xy_size_compensation.value)); | ||||
|             if (layer->regions.size() == 1) { | ||||
|                 // single region
 | ||||
|                 LayerRegion *layerm = layer->regions.front(); | ||||
|                 layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stInternal); | ||||
|             } else { | ||||
|                 if (delta < 0) { | ||||
|                     // multiple regions, shrinking
 | ||||
|                     // we apply the offset to the combined shape, then intersect it
 | ||||
|                     // with the original slices for each region
 | ||||
|                     Polygons region_slices; | ||||
|                     for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) | ||||
|                         polygons_append(region_slices, layer->regions[region_id]->slices.surfaces); | ||||
|                     Polygons slices = offset(union_(region_slices), delta); | ||||
|                     for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) { | ||||
|                         LayerRegion *layerm = layer->regions[region_id]; | ||||
|                         layerm->slices.set(std::move(intersection_ex(slices, to_polygons(std::move(layerm->slices.surfaces)))), stInternal); | ||||
|                     } | ||||
|                 } else { | ||||
|                     // multiple regions, growing
 | ||||
|                     // this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap
 | ||||
|                     // so we give priority to the first one and so on
 | ||||
|                     Polygons processed; | ||||
|                     for (size_t region_id = 0;; ++ region_id) { | ||||
|                         LayerRegion *layerm = layer->regions[region_id]; | ||||
|                         ExPolygons slices = offset_ex(to_expolygons(layerm->slices.surfaces), delta); | ||||
|                         if (region_id > 0) | ||||
|                             // Trim by the slices of already processed regions.
 | ||||
|                             slices = diff_ex(to_polygons(std::move(slices)), processed); | ||||
|                         if (region_id + 1 == layer->regions.size()) { | ||||
|                             layerm->slices.set(std::move(slices), stInternal); | ||||
|                             break; | ||||
|                         } | ||||
|                         polygons_append(processed, slices); | ||||
|                         layerm->slices.set(std::move(slices), stInternal); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         // Merge all regions' slices to get islands, chain them by a shortest path.
 | ||||
|         layer->make_slices(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier) | ||||
| { | ||||
|     std::vector<ExPolygons> layers; | ||||
|     assert(region_id < this->region_volumes.size()); | ||||
|     std::vector<int> &volumes = this->region_volumes[region_id]; | ||||
|     if (! volumes.empty()) { | ||||
|         // Compose mesh.
 | ||||
|         //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
 | ||||
|         TriangleMesh mesh; | ||||
|         for (std::vector<int>::const_iterator it_volume = volumes.begin(); it_volume != volumes.end(); ++ it_volume) { | ||||
|             ModelVolume *volume = this->model_object()->volumes[*it_volume]; | ||||
|             if (volume->modifier == modifier) | ||||
|                 mesh.merge(volume->mesh); | ||||
|         } | ||||
|         if (mesh.stl.stats.number_of_facets > 0) { | ||||
|             // transform mesh
 | ||||
|             // we ignore the per-instance transformations currently and only 
 | ||||
|             // consider the first one
 | ||||
|             this->model_object()->instances.front()->transform_mesh(&mesh, true); | ||||
|             // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
 | ||||
|             mesh.translate(- unscale(this->_copies_shift.x), - unscale(this->_copies_shift.y), -this->model_object()->bounding_box().min.z); | ||||
|             // perform actual slicing
 | ||||
|             TriangleMeshSlicer mslicer(&mesh); | ||||
|             mslicer.slice(z, &layers); | ||||
|         } | ||||
|     } | ||||
|     return layers; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| PrintObject::_make_perimeters() | ||||
| { | ||||
|     if (this->state.is_done(posPerimeters)) return; | ||||
|     this->state.set_started(posPerimeters); | ||||
|      | ||||
|     // merge slices if they were split into types
 | ||||
|     if (this->typed_slices) { | ||||
|         FOREACH_LAYER(this, layer_it) | ||||
|             (*layer_it)->merge_slices(); | ||||
|         this->typed_slices = false; | ||||
|         this->state.invalidate(posPrepareInfill); | ||||
|     } | ||||
|      | ||||
|     // compare each layer to the one below, and mark those slices needing
 | ||||
|     // one additional inner perimeter, like the top of domed objects-
 | ||||
|      | ||||
|     // this algorithm makes sure that at least one perimeter is overlapping
 | ||||
|     // but we don't generate any extra perimeter if fill density is zero, as they would be floating
 | ||||
|     // inside the object - infill_only_where_needed should be the method of choice for printing
 | ||||
|     // hollow objects
 | ||||
|     FOREACH_REGION(this->_print, region_it) { | ||||
|         size_t region_id = region_it - this->_print->regions.begin(); | ||||
|         const PrintRegion ®ion = **region_it; | ||||
|          | ||||
|          | ||||
|         if (!region.config.extra_perimeters | ||||
|             || region.config.perimeters == 0 | ||||
|             || region.config.fill_density == 0 | ||||
|             || this->layer_count() < 2) continue; | ||||
|          | ||||
|         for (int i = 0; i < int(this->layer_count()) - 1; ++i) { | ||||
|             LayerRegion &layerm                     = *this->get_layer(i)->get_region(region_id); | ||||
|             const LayerRegion &upper_layerm         = *this->get_layer(i+1)->get_region(region_id); | ||||
|             const Polygons upper_layerm_polygons    = upper_layerm.slices; | ||||
|              | ||||
|             // Filter upper layer polygons in intersection_ppl by their bounding boxes?
 | ||||
|             // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
 | ||||
|             double total_loop_length = 0; | ||||
|             for (Polygons::const_iterator it = upper_layerm_polygons.begin(); it != upper_layerm_polygons.end(); ++it) | ||||
|                 total_loop_length += it->length(); | ||||
|              | ||||
|             const coord_t perimeter_spacing     = layerm.flow(frPerimeter).scaled_spacing(); | ||||
|             const Flow ext_perimeter_flow       = layerm.flow(frExternalPerimeter); | ||||
|             const coord_t ext_perimeter_width   = ext_perimeter_flow.scaled_width(); | ||||
|             const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing(); | ||||
|              | ||||
|             for (Surfaces::iterator slice = layerm.slices.surfaces.begin(); | ||||
|                 slice != layerm.slices.surfaces.end(); ++slice) { | ||||
|                 while (true) { | ||||
|                     // compute the total thickness of perimeters
 | ||||
|                     const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 | ||||
|                         + (region.config.perimeters-1 + region.config.extra_perimeters) * perimeter_spacing; | ||||
|                      | ||||
|                     // define a critical area where we don't want the upper slice to fall into
 | ||||
|                     // (it should either lay over our perimeters or outside this area)
 | ||||
|                     const coord_t critical_area_depth = perimeter_spacing * 1.5; | ||||
|                     const Polygons critical_area = diff( | ||||
|                         offset(slice->expolygon, -perimeters_thickness), | ||||
|                         offset(slice->expolygon, -(perimeters_thickness + critical_area_depth)) | ||||
|                     ); | ||||
|                      | ||||
|                     // check whether a portion of the upper slices falls inside the critical area
 | ||||
|                     const Polylines intersection = intersection_pl( | ||||
|                         to_polylines(upper_layerm_polygons), | ||||
|                         critical_area | ||||
|                     ); | ||||
|                      | ||||
|                     // only add an additional loop if at least 30% of the slice loop would benefit from it
 | ||||
|                     { | ||||
|                         double total_intersection_length = 0; | ||||
|                         for (Polylines::const_iterator it = intersection.begin(); it != intersection.end(); ++it) | ||||
|                             total_intersection_length += it->length(); | ||||
|                         if (total_intersection_length <= total_loop_length*0.3) break; | ||||
|                     } | ||||
|                      | ||||
|                     /*
 | ||||
|                     if (0) { | ||||
|                         require "Slic3r/SVG.pm"; | ||||
|                         Slic3r::SVG::output( | ||||
|                             "extra.svg", | ||||
|                             no_arrows   => 1, | ||||
|                             expolygons  => union_ex($critical_area), | ||||
|                             polylines   => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], | ||||
|                         ); | ||||
|                     } | ||||
|                     */ | ||||
|                      | ||||
|                     slice->extra_perimeters++; | ||||
|                 } | ||||
|                  | ||||
|                 #ifdef DEBUG | ||||
|                     if (slice->extra_perimeters > 0) | ||||
|                         printf("  adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, i); | ||||
|                 #endif | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     parallelize<Layer*>( | ||||
|         std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())),  // cast LayerPtrs to std::queue<Layer*>
 | ||||
|         boost::bind(&Slic3r::Layer::make_perimeters, _1), | ||||
|         this->_print->config.threads.value | ||||
|     ); | ||||
|      | ||||
|     /*
 | ||||
|         simplify slices (both layer and region slices), | ||||
|         we only need the max resolution for perimeters | ||||
|     ### This makes this method not-idempotent, so we keep it disabled for now. | ||||
|     ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); | ||||
|     */ | ||||
|      | ||||
|     this->state.set_done(posPerimeters); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| PrintObject::_infill() | ||||
| { | ||||
|     if (this->state.is_done(posInfill)) return; | ||||
|     this->state.set_started(posInfill); | ||||
|      | ||||
|     parallelize<Layer*>( | ||||
|         std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())),  // cast LayerPtrs to std::queue<Layer*>
 | ||||
|         boost::bind(&Slic3r::Layer::make_fills, _1), | ||||
|         this->_print->config.threads.value | ||||
|     ); | ||||
|      | ||||
|     /*  we could free memory now, but this would make this step not idempotent
 | ||||
|     ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; | ||||
|     */ | ||||
|      | ||||
|     this->state.set_done(posInfill); | ||||
| } | ||||
| 
 | ||||
| void PrintObject::_generate_support_material() | ||||
| { | ||||
|     PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters()); | ||||
|     support_material.generate(*this); | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ namespace Slic3r { | |||
| 
 | ||||
| class SVG | ||||
| { | ||||
|     public: | ||||
| public: | ||||
|     bool arrows; | ||||
|     std::string fill, stroke; | ||||
|     Point origin; | ||||
|  | @ -89,6 +89,10 @@ public: | |||
|     static void export_expolygons(const char *path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0); | ||||
|     static void export_expolygons(const std::string &path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0)  | ||||
|         { export_expolygons(path.c_str(), bbox, expolygons, stroke_outer, stroke_holes, stroke_width); } | ||||
|     static void export_expolygons(const char *path, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0) | ||||
|         { export_expolygons(path, get_extents(expolygons), expolygons, stroke_outer, stroke_holes, stroke_width); } | ||||
|     static void export_expolygons(const std::string &path, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0)  | ||||
|         { export_expolygons(path.c_str(), get_extents(expolygons), expolygons, stroke_outer, stroke_holes, stroke_width); } | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										631
									
								
								xs/src/libslic3r/Slicing.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										631
									
								
								xs/src/libslic3r/Slicing.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,631 @@ | |||
| #include "Slicing.hpp" | ||||
| #include "SlicingAdaptive.hpp" | ||||
| #include "PrintConfig.hpp" | ||||
| #include "Model.hpp" | ||||
| 
 | ||||
| // #define SLIC3R_DEBUG
 | ||||
| 
 | ||||
| // Make assert active if SLIC3R_DEBUG
 | ||||
| #ifdef SLIC3R_DEBUG | ||||
|     #undef NDEBUG | ||||
|     #define DEBUG | ||||
|     #define _DEBUG | ||||
|     #include "SVG.hpp" | ||||
|     #undef assert  | ||||
|     #include <cassert> | ||||
| #endif | ||||
| 
 | ||||
| namespace Slic3r | ||||
| { | ||||
| 
 | ||||
| SlicingParameters SlicingParameters::create_from_config( | ||||
| 	const PrintConfig 		&print_config,  | ||||
| 	const PrintObjectConfig &object_config, | ||||
| 	coordf_t				 object_height, | ||||
| 	const std::set<size_t>  &object_extruders) | ||||
| { | ||||
|     coordf_t first_layer_height                      = (object_config.first_layer_height.value <= 0) ?  | ||||
|         object_config.layer_height.value :  | ||||
|         object_config.first_layer_height.get_abs_value(object_config.layer_height.value); | ||||
|     coordf_t support_material_extruder_dmr           = print_config.nozzle_diameter.get_at(object_config.support_material_extruder.value - 1); | ||||
|     coordf_t support_material_interface_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_interface_extruder.value - 1); | ||||
|     bool     soluble_interface                       = object_config.support_material_contact_distance.value == 0.; | ||||
| 
 | ||||
|     SlicingParameters params; | ||||
|     params.layer_height = object_config.layer_height.value; | ||||
|     params.first_print_layer_height = first_layer_height; | ||||
|     params.first_object_layer_height = first_layer_height; | ||||
|     params.object_print_z_min = 0.; | ||||
|     params.object_print_z_max = object_height; | ||||
|     params.base_raft_layers = object_config.raft_layers.value; | ||||
|     params.soluble_interface = soluble_interface; | ||||
| 
 | ||||
|     if (! soluble_interface) { | ||||
|         params.gap_raft_object    = object_config.support_material_contact_distance.value; | ||||
|         params.gap_object_support = object_config.support_material_contact_distance.value; | ||||
|         params.gap_support_object = object_config.support_material_contact_distance.value; | ||||
|     } | ||||
| 
 | ||||
|     if (params.base_raft_layers > 0) { | ||||
| 		params.interface_raft_layers = (params.base_raft_layers + 1) / 2; | ||||
|         params.base_raft_layers -= params.interface_raft_layers; | ||||
|         // Use as large as possible layer height for the intermediate raft layers.
 | ||||
|         params.base_raft_layer_height       = std::max(params.layer_height, 0.75 * support_material_extruder_dmr); | ||||
|         params.interface_raft_layer_height  = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr); | ||||
|         params.contact_raft_layer_height_bridging = false; | ||||
|         params.first_object_layer_bridging  = false; | ||||
|         #if 1 | ||||
|         params.contact_raft_layer_height    = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr); | ||||
|         if (! soluble_interface) { | ||||
|             // Compute the average of all nozzles used for printing the object over a raft.
 | ||||
|             //FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa?
 | ||||
|             coordf_t average_object_extruder_dmr = 0.; | ||||
|             if (! object_extruders.empty()) { | ||||
|                 for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder) | ||||
|                     average_object_extruder_dmr += print_config.nozzle_diameter.get_at(*it_extruder); | ||||
|                 average_object_extruder_dmr /= coordf_t(object_extruders.size()); | ||||
|             } | ||||
|             params.first_object_layer_height   = average_object_extruder_dmr; | ||||
|             params.first_object_layer_bridging = true; | ||||
|         } | ||||
|         #else | ||||
|         params.contact_raft_layer_height    = soluble_interface ? support_material_interface_extruder_dmr : 0.75 * support_material_interface_extruder_dmr; | ||||
|         params.contact_raft_layer_height_bridging = ! soluble_interface; | ||||
|         ... | ||||
|         #endif | ||||
|     } | ||||
| 
 | ||||
|     if (params.has_raft()) { | ||||
|         // Raise first object layer Z by the thickness of the raft itself plus the extra distance required by the support material logic.
 | ||||
|         //FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case.
 | ||||
| 		if (params.raft_layers() == 1) { | ||||
|             // There is only the contact layer.
 | ||||
| 			params.contact_raft_layer_height = first_layer_height; | ||||
|             params.raft_contact_top_z = first_layer_height; | ||||
| 		} else { | ||||
|             assert(params.base_raft_layers > 0); | ||||
|             assert(params.interface_raft_layers > 0); | ||||
|             // Number of the base raft layers is decreased by the first layer.
 | ||||
|             params.raft_base_top_z       = first_layer_height + coordf_t(params.base_raft_layers - 1) * params.base_raft_layer_height; | ||||
|             // Number of the interface raft layers is decreased by the contact layer.
 | ||||
|             params.raft_interface_top_z  = params.raft_base_top_z + coordf_t(params.interface_raft_layers - 1) * params.interface_raft_layer_height; | ||||
| 			params.raft_contact_top_z    = params.raft_interface_top_z + params.contact_raft_layer_height; | ||||
| 		} | ||||
|         coordf_t print_z = params.raft_contact_top_z + params.gap_raft_object; | ||||
|         params.object_print_z_min  = print_z; | ||||
|         params.object_print_z_max += print_z; | ||||
|     } | ||||
| 
 | ||||
|     params.min_layer_height = std::min(params.layer_height, first_layer_height); | ||||
|     params.max_layer_height = std::max(params.layer_height, first_layer_height); | ||||
| 
 | ||||
|     //FIXME add it to the print configuration
 | ||||
|     params.min_layer_height = 0.05; | ||||
| 
 | ||||
|     // Calculate the maximum layer height as 0.75 from the minimum nozzle diameter.
 | ||||
|     if (! object_extruders.empty()) { | ||||
|         coordf_t min_object_extruder_dmr = 1000000.; | ||||
|         for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder) | ||||
|             min_object_extruder_dmr = std::min(min_object_extruder_dmr, print_config.nozzle_diameter.get_at(*it_extruder)); | ||||
|         // Allow excessive maximum layer height higher than 0.75 * min_object_extruder_dmr 
 | ||||
|         params.max_layer_height = std::max(std::max(params.layer_height, first_layer_height), 0.75 * min_object_extruder_dmr); | ||||
|     } | ||||
| 
 | ||||
|     return params; | ||||
| } | ||||
| 
 | ||||
| // Convert layer_height_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
 | ||||
| // in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation.
 | ||||
| std::vector<coordf_t> layer_height_profile_from_ranges( | ||||
| 	const SlicingParameters 	&slicing_params, | ||||
| 	const t_layer_height_ranges &layer_height_ranges) | ||||
| { | ||||
|     // 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed.
 | ||||
|     std::vector<std::pair<t_layer_height_range,coordf_t>> ranges_non_overlapping; | ||||
|     ranges_non_overlapping.reserve(layer_height_ranges.size() * 4); | ||||
|     if (slicing_params.first_object_layer_height_fixed()) | ||||
|         ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>( | ||||
|             t_layer_height_range(0., slicing_params.first_object_layer_height),  | ||||
|             slicing_params.first_object_layer_height)); | ||||
|     // The height ranges are sorted lexicographically by low / high layer boundaries.
 | ||||
|     for (t_layer_height_ranges::const_iterator it_range = layer_height_ranges.begin(); it_range != layer_height_ranges.end(); ++ it_range) { | ||||
|         coordf_t lo = it_range->first.first; | ||||
|         coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height()); | ||||
|         coordf_t height = it_range->second; | ||||
|         if (! ranges_non_overlapping.empty()) | ||||
|             // Trim current low with the last high.
 | ||||
|             lo = std::max(lo, ranges_non_overlapping.back().first.second); | ||||
|         if (lo + EPSILON < hi) | ||||
|             // Ignore too narrow ranges.
 | ||||
|             ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(t_layer_height_range(lo, hi), height)); | ||||
|     } | ||||
| 
 | ||||
|     // 2) Convert the trimmed ranges to a height profile, fill in the undefined intervals between z=0 and z=slicing_params.object_print_z_max()
 | ||||
|     // with slicing_params.layer_height
 | ||||
|     std::vector<coordf_t> layer_height_profile; | ||||
|     for (std::vector<std::pair<t_layer_height_range,coordf_t>>::const_iterator it_range = ranges_non_overlapping.begin(); it_range != ranges_non_overlapping.end(); ++ it_range) { | ||||
|         coordf_t lo = it_range->first.first; | ||||
|         coordf_t hi = it_range->first.second; | ||||
|         coordf_t height = it_range->second; | ||||
|         coordf_t last_z      = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2]; | ||||
|         coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1]; | ||||
|         if (lo > last_z + EPSILON) { | ||||
|             // Insert a step of normal layer height.
 | ||||
|             layer_height_profile.push_back(last_z); | ||||
|             layer_height_profile.push_back(slicing_params.layer_height); | ||||
|             layer_height_profile.push_back(lo); | ||||
|             layer_height_profile.push_back(slicing_params.layer_height); | ||||
|         } | ||||
|         // Insert a step of the overriden layer height.
 | ||||
|         layer_height_profile.push_back(lo); | ||||
|         layer_height_profile.push_back(height); | ||||
|         layer_height_profile.push_back(hi); | ||||
|         layer_height_profile.push_back(height); | ||||
|     } | ||||
| 
 | ||||
|     coordf_t last_z      = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2]; | ||||
|     coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1]; | ||||
|     if (last_z < slicing_params.object_print_z_height()) { | ||||
|         // Insert a step of normal layer height up to the object top.
 | ||||
|         layer_height_profile.push_back(last_z); | ||||
|         layer_height_profile.push_back(slicing_params.layer_height); | ||||
|         layer_height_profile.push_back(slicing_params.object_print_z_height()); | ||||
|         layer_height_profile.push_back(slicing_params.layer_height); | ||||
|     } | ||||
| 
 | ||||
|    	return layer_height_profile; | ||||
| } | ||||
| 
 | ||||
| // Based on the work of @platsch
 | ||||
| // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
 | ||||
| std::vector<coordf_t> layer_height_profile_adaptive( | ||||
|     const SlicingParameters     &slicing_params, | ||||
|     const t_layer_height_ranges &layer_height_ranges, | ||||
|     const ModelVolumePtrs		&volumes) | ||||
| { | ||||
|     // 1) Initialize the SlicingAdaptive class with the object meshes.
 | ||||
|     SlicingAdaptive as; | ||||
|     as.set_slicing_parameters(slicing_params); | ||||
|     for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it) | ||||
|         if (! (*it)->modifier) | ||||
|             as.add_mesh(&(*it)->mesh); | ||||
|     as.prepare(); | ||||
| 
 | ||||
|     // 2) Generate layers using the algorithm of @platsch 
 | ||||
|     // loop until we have at least one layer and the max slice_z reaches the object height
 | ||||
|     //FIXME make it configurable
 | ||||
|     // Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm.
 | ||||
|     const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value');
 | ||||
| 
 | ||||
|     std::vector<coordf_t> layer_height_profile; | ||||
|     layer_height_profile.push_back(0.); | ||||
|     layer_height_profile.push_back(slicing_params.first_object_layer_height); | ||||
|     if (slicing_params.first_object_layer_height_fixed()) { | ||||
|         layer_height_profile.push_back(slicing_params.first_object_layer_height); | ||||
|         layer_height_profile.push_back(slicing_params.first_object_layer_height); | ||||
|     } | ||||
|     coordf_t slice_z = slicing_params.first_object_layer_height; | ||||
|     coordf_t height  = slicing_params.first_object_layer_height; | ||||
|     coordf_t cusp_height = 0.; | ||||
|     int current_facet = 0; | ||||
|     while ((slice_z - height) <= slicing_params.object_print_z_height()) { | ||||
|         height = 999; | ||||
|         // Slic3r::debugf "\n Slice layer: %d\n", $id;
 | ||||
|         // determine next layer height
 | ||||
|         coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet); | ||||
|         // check for horizontal features and object size
 | ||||
|         /*
 | ||||
|         if($self->config->get_value('match_horizontal_surfaces')) { | ||||
|             my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height); | ||||
|             if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) { | ||||
|                 Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist; | ||||
|                 # can we shrink the current layer a bit? | ||||
|                 if($cusp_height-($min_height-$horizontal_dist) > $min_height) { | ||||
|                     # yes we can | ||||
|                     $cusp_height = $cusp_height-($min_height-$horizontal_dist); | ||||
|                     Slic3r::debugf "Shrink layer height to %f\n", $cusp_height; | ||||
|                 }else{ | ||||
|                     # no, current layer would become too thin | ||||
|                     $cusp_height = $cusp_height+$horizontal_dist; | ||||
|                     Slic3r::debugf "Widen layer height to %f\n", $cusp_height; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         */ | ||||
|         height = std::min(cusp_height, height); | ||||
| 
 | ||||
|         // apply z-gradation
 | ||||
|         /*
 | ||||
|         my $gradation = $self->config->get_value('adaptive_slicing_z_gradation'); | ||||
|         if($gradation > 0) { | ||||
|             $height = $height - unscale((scale($height)) % (scale($gradation))); | ||||
|         } | ||||
|         */ | ||||
|      | ||||
|         // look for an applicable custom range
 | ||||
|         /*
 | ||||
|         if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) { | ||||
|             $height = $range->[2]; | ||||
|      | ||||
|             # if user set custom height to zero we should just skip the range and resume slicing over it | ||||
|             if ($height == 0) { | ||||
|                 $slice_z += $range->[1] - $range->[0]; | ||||
|                 next; | ||||
|             } | ||||
|         } | ||||
|         */ | ||||
|          | ||||
|         layer_height_profile.push_back(slice_z); | ||||
|         layer_height_profile.push_back(height); | ||||
|         slice_z += height; | ||||
|         layer_height_profile.push_back(slice_z); | ||||
|         layer_height_profile.push_back(height); | ||||
|     } | ||||
| 
 | ||||
|     coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); | ||||
|     layer_height_profile.push_back(last); | ||||
|     layer_height_profile.push_back(slicing_params.first_object_layer_height); | ||||
|     layer_height_profile.push_back(slicing_params.object_print_z_height()); | ||||
|     layer_height_profile.push_back(slicing_params.first_object_layer_height); | ||||
| 
 | ||||
|     return layer_height_profile; | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| static inline T clamp(const T low, const T high, const T value) | ||||
| { | ||||
|     return std::max(low, std::min(high, value)); | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| static inline T lerp(const T a, const T b, const T t) | ||||
| { | ||||
|     assert(t >= T(-EPSILON) && t <= T(1.+EPSILON)); | ||||
|     return (1. - t) * a + t * b; | ||||
| } | ||||
| 
 | ||||
| void adjust_layer_height_profile( | ||||
|     const SlicingParameters     &slicing_params, | ||||
|     std::vector<coordf_t> 		&layer_height_profile, | ||||
|     coordf_t 					 z, | ||||
|     coordf_t 					 layer_thickness_delta, | ||||
|     coordf_t 					 band_width, | ||||
|     LayerHeightEditActionType    action) | ||||
| { | ||||
|      // Constrain the profile variability by the 1st layer height.
 | ||||
|     std::pair<coordf_t, coordf_t> z_span_variable =  | ||||
|         std::pair<coordf_t, coordf_t>( | ||||
|             slicing_params.first_object_layer_height_fixed() ? slicing_params.first_object_layer_height : 0., | ||||
|             slicing_params.object_print_z_height()); | ||||
|     if (z < z_span_variable.first || z > z_span_variable.second) | ||||
|         return; | ||||
| 
 | ||||
| 	assert(layer_height_profile.size() >= 2); | ||||
| 
 | ||||
|     // 1) Get the current layer thickness at z.
 | ||||
|     coordf_t current_layer_height = slicing_params.layer_height; | ||||
|     for (size_t i = 0; i < layer_height_profile.size(); i += 2) { | ||||
|         if (i + 2 == layer_height_profile.size()) { | ||||
|             current_layer_height = layer_height_profile[i + 1]; | ||||
|             break; | ||||
|         } else if (layer_height_profile[i + 2] > z) { | ||||
|             coordf_t z1 = layer_height_profile[i]; | ||||
|             coordf_t h1 = layer_height_profile[i + 1]; | ||||
|             coordf_t z2 = layer_height_profile[i + 2]; | ||||
|             coordf_t h2 = layer_height_profile[i + 3]; | ||||
|             current_layer_height = lerp(h1, h2, (z - z1) / (z2 - z1)); | ||||
| 			break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // 2) Is it possible to apply the delta?
 | ||||
|     switch (action) { | ||||
|         case LAYER_HEIGHT_EDIT_ACTION_DECREASE: | ||||
|             layer_thickness_delta = - layer_thickness_delta; | ||||
|             // fallthrough
 | ||||
|         case LAYER_HEIGHT_EDIT_ACTION_INCREASE: | ||||
|             if (layer_thickness_delta > 0) { | ||||
|                 if (current_layer_height >= slicing_params.max_layer_height - EPSILON) | ||||
|                     return; | ||||
|                 layer_thickness_delta = std::min(layer_thickness_delta, slicing_params.max_layer_height - current_layer_height); | ||||
|             } else { | ||||
|                 if (current_layer_height <= slicing_params.min_layer_height + EPSILON) | ||||
|                     return; | ||||
|                 layer_thickness_delta = std::max(layer_thickness_delta, slicing_params.min_layer_height - current_layer_height); | ||||
|             } | ||||
|             break; | ||||
|         case LAYER_HEIGHT_EDIT_ACTION_REDUCE: | ||||
|         case LAYER_HEIGHT_EDIT_ACTION_SMOOTH: | ||||
|             layer_thickness_delta = std::abs(layer_thickness_delta); | ||||
|             layer_thickness_delta = std::min(layer_thickness_delta, std::abs(slicing_params.layer_height - current_layer_height)); | ||||
|             if (layer_thickness_delta < EPSILON) | ||||
|                 return; | ||||
|             break; | ||||
|         default: | ||||
|             assert(false); | ||||
|             break; | ||||
|     } | ||||
| 
 | ||||
|     // 3) Densify the profile inside z +- band_width/2, remove duplicate Zs from the height profile inside the band.
 | ||||
| 	coordf_t lo = std::max(z_span_variable.first,  z - 0.5 * band_width); | ||||
| 	coordf_t hi = std::min(z_span_variable.second, z + 0.5 * band_width); | ||||
|     coordf_t z_step = 0.1; | ||||
|     size_t i = 0; | ||||
|     while (i < layer_height_profile.size() && layer_height_profile[i] < lo) | ||||
|         i += 2; | ||||
|     i -= 2; | ||||
| 
 | ||||
|     std::vector<double> profile_new; | ||||
|     profile_new.reserve(layer_height_profile.size()); | ||||
| 	assert(i >= 0 && i + 1 < layer_height_profile.size()); | ||||
| 	profile_new.insert(profile_new.end(), layer_height_profile.begin(), layer_height_profile.begin() + i + 2); | ||||
|     coordf_t zz = lo; | ||||
|     size_t i_resampled_start = profile_new.size(); | ||||
|     while (zz < hi) { | ||||
|         size_t next = i + 2; | ||||
|         coordf_t z1 = layer_height_profile[i]; | ||||
|         coordf_t h1 = layer_height_profile[i + 1]; | ||||
|         coordf_t height = h1; | ||||
|         if (next < layer_height_profile.size()) { | ||||
|             coordf_t z2 = layer_height_profile[next]; | ||||
|             coordf_t h2 = layer_height_profile[next + 1]; | ||||
|             height = lerp(h1, h2, (zz - z1) / (z2 - z1)); | ||||
|         } | ||||
|         // Adjust height by layer_thickness_delta.
 | ||||
|         coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.; | ||||
|         coordf_t height_new = height; | ||||
|         switch (action) { | ||||
|             case LAYER_HEIGHT_EDIT_ACTION_INCREASE: | ||||
|             case LAYER_HEIGHT_EDIT_ACTION_DECREASE: | ||||
|                 height += weight * layer_thickness_delta; | ||||
|                 break; | ||||
|             case LAYER_HEIGHT_EDIT_ACTION_REDUCE: | ||||
|             { | ||||
|                 coordf_t delta = height - slicing_params.layer_height; | ||||
|                 coordf_t step  = weight * layer_thickness_delta; | ||||
|                 step = (std::abs(delta) > step) ? | ||||
|                     (delta > 0) ? -step : step : | ||||
|                     -delta; | ||||
|                 height += step; | ||||
|                 break; | ||||
|             } | ||||
|             case LAYER_HEIGHT_EDIT_ACTION_SMOOTH: | ||||
|             { | ||||
|                 // Don't modify the profile during resampling process, do it at the next step.
 | ||||
|                 break; | ||||
|             } | ||||
|             default: | ||||
|                 assert(false); | ||||
|                 break; | ||||
|         } | ||||
|         // Avoid entering a too short segment.
 | ||||
|         if (profile_new[profile_new.size() - 2] + EPSILON < zz) { | ||||
|             profile_new.push_back(zz); | ||||
|             profile_new.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, height)); | ||||
|         } | ||||
|         zz += z_step; | ||||
|         i = next; | ||||
|         while (i < layer_height_profile.size() && layer_height_profile[i] < zz) | ||||
|             i += 2; | ||||
|         i -= 2; | ||||
|     } | ||||
| 
 | ||||
|     i += 2; | ||||
|     assert(i > 0); | ||||
|     size_t i_resampled_end = profile_new.size(); | ||||
| 	if (i < layer_height_profile.size()) { | ||||
|         assert(zz >= layer_height_profile[i - 2]); | ||||
|         assert(zz <= layer_height_profile[i]); | ||||
| //        profile_new.push_back(zz);
 | ||||
| //        profile_new.push_back(layer_height_profile[i + 1]);
 | ||||
| 		profile_new.insert(profile_new.end(), layer_height_profile.begin() + i, layer_height_profile.end()); | ||||
|     } | ||||
|     layer_height_profile = std::move(profile_new); | ||||
| 
 | ||||
|     if (action == LAYER_HEIGHT_EDIT_ACTION_SMOOTH) { | ||||
|         size_t n_rounds = 6; | ||||
|         for (size_t i_round = 0; i_round < n_rounds; ++ i_round) { | ||||
|             profile_new = layer_height_profile; | ||||
|             for (size_t i = i_resampled_start; i < i_resampled_end; i += 2) { | ||||
|                 coordf_t zz = profile_new[i]; | ||||
|                 coordf_t t = std::abs(zz - z) < 0.5 * band_width ? (0.25 + 0.25 * cos(2. * M_PI * (zz - z) / band_width)) : 0.; | ||||
|                 assert(t >= 0. && t <= 0.5000001); | ||||
|                 if (i == 0) | ||||
|                     layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i + 3]; | ||||
|                 else if (i + 1 == profile_new.size()) | ||||
|                     layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i - 1]; | ||||
|                 else | ||||
|                     layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + 0.5 * t * (profile_new[i - 1] + profile_new[i + 3]); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 	assert(layer_height_profile.size() > 2); | ||||
| 	assert(layer_height_profile.size() % 2 == 0); | ||||
| 	assert(layer_height_profile[0] == 0.); | ||||
| #ifdef _DEBUG | ||||
| 	for (size_t i = 2; i < layer_height_profile.size(); i += 2) | ||||
| 		assert(layer_height_profile[i - 2] <= layer_height_profile[i]); | ||||
| 	for (size_t i = 1; i < layer_height_profile.size(); i += 2) { | ||||
| 		assert(layer_height_profile[i] > slicing_params.min_layer_height - EPSILON); | ||||
| 		assert(layer_height_profile[i] < slicing_params.max_layer_height + EPSILON); | ||||
| 	} | ||||
| #endif /* _DEBUG */ | ||||
| } | ||||
| 
 | ||||
| // Produce object layers as pairs of low / high layer boundaries, stored into a linear vector.
 | ||||
| std::vector<coordf_t> generate_object_layers( | ||||
| 	const SlicingParameters 	&slicing_params, | ||||
| 	const std::vector<coordf_t> &layer_height_profile) | ||||
| { | ||||
|     coordf_t print_z = 0; | ||||
|     coordf_t height  = 0; | ||||
| 
 | ||||
|     std::vector<coordf_t> out; | ||||
| 
 | ||||
|     if (slicing_params.first_object_layer_height_fixed()) { | ||||
|         out.push_back(0); | ||||
|         print_z = slicing_params.first_object_layer_height; | ||||
|         out.push_back(print_z); | ||||
|     } | ||||
| 
 | ||||
|     size_t idx_layer_height_profile = 0; | ||||
|     // loop until we have at least one layer and the max slice_z reaches the object height
 | ||||
|     coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height; | ||||
|     while (slice_z < slicing_params.object_print_z_height()) { | ||||
|         height = slicing_params.min_layer_height; | ||||
|         if (idx_layer_height_profile < layer_height_profile.size()) { | ||||
|             size_t next = idx_layer_height_profile + 2; | ||||
|             for (;;) { | ||||
|                 if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next]) | ||||
|                     break; | ||||
|                 idx_layer_height_profile = next; | ||||
|                 next += 2; | ||||
|             } | ||||
|             coordf_t z1 = layer_height_profile[idx_layer_height_profile]; | ||||
|             coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1]; | ||||
|             height = h1; | ||||
|             if (next < layer_height_profile.size()) { | ||||
|                 coordf_t z2 = layer_height_profile[next]; | ||||
|                 coordf_t h2 = layer_height_profile[next + 1]; | ||||
|                 height = lerp(h1, h2, (slice_z - z1) / (z2 - z1)); | ||||
|                 assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON); | ||||
|             } | ||||
|         } | ||||
|         slice_z = print_z + 0.5 * height; | ||||
|         if (slice_z >= slicing_params.object_print_z_height()) | ||||
|             break; | ||||
|         assert(height > slicing_params.min_layer_height - EPSILON); | ||||
|         assert(height < slicing_params.max_layer_height + EPSILON); | ||||
|         out.push_back(print_z); | ||||
|         print_z += height; | ||||
|         slice_z = print_z + 0.5 * slicing_params.min_layer_height; | ||||
|         out.push_back(print_z); | ||||
|     } | ||||
| 
 | ||||
|     //FIXME Adjust the last layer to align with the top object layer exactly?
 | ||||
|     return out; | ||||
| } | ||||
| 
 | ||||
| int generate_layer_height_texture( | ||||
| 	const SlicingParameters 	&slicing_params, | ||||
| 	const std::vector<coordf_t> &layers, | ||||
| 	void *data, int rows, int cols, bool level_of_detail_2nd_level) | ||||
| { | ||||
| // https://github.com/aschn/gnuplot-colorbrewer
 | ||||
|     std::vector<Point3> palette_raw; | ||||
|     palette_raw.push_back(Point3(0x0B2, 0x018, 0x02B)); | ||||
|     palette_raw.push_back(Point3(0x0D6, 0x060, 0x04D)); | ||||
|     palette_raw.push_back(Point3(0x0F4, 0x0A5, 0x082)); | ||||
|     palette_raw.push_back(Point3(0x0FD, 0x0DB, 0x0C7)); | ||||
|     palette_raw.push_back(Point3(0x0D1, 0x0E5, 0x0F0)); | ||||
|     palette_raw.push_back(Point3(0x092, 0x0C5, 0x0DE)); | ||||
|     palette_raw.push_back(Point3(0x043, 0x093, 0x0C3)); | ||||
|     palette_raw.push_back(Point3(0x021, 0x066, 0x0AC)); | ||||
| 
 | ||||
|     // Clear the main texture and the 2nd LOD level.
 | ||||
|     memset(data, 0, rows * cols * 5); | ||||
|     // 2nd LOD level data start
 | ||||
|     unsigned char *data1 = reinterpret_cast<unsigned char*>(data) + rows * cols * 4; | ||||
|     int ncells  = std::min((cols-1) * rows, int(ceil(16. * (slicing_params.object_print_z_height() / slicing_params.min_layer_height)))); | ||||
|     int ncells1 = ncells / 2; | ||||
|     int cols1   = cols / 2; | ||||
|     coordf_t z_to_cell = coordf_t(ncells-1) / slicing_params.object_print_z_height(); | ||||
|     coordf_t cell_to_z = slicing_params.object_print_z_height() / coordf_t(ncells-1); | ||||
|     coordf_t z_to_cell1 = coordf_t(ncells1-1) / slicing_params.object_print_z_height(); | ||||
|     coordf_t cell_to_z1 = slicing_params.object_print_z_height() / coordf_t(ncells1-1); | ||||
|     // for color scaling
 | ||||
| 	coordf_t hscale = 2.f * std::max(slicing_params.max_layer_height - slicing_params.layer_height, slicing_params.layer_height - slicing_params.min_layer_height); | ||||
| 	if (hscale == 0) | ||||
| 		// All layers have the same height. Provide some height scale to avoid division by zero.
 | ||||
| 		hscale = slicing_params.layer_height; | ||||
|     for (size_t idx_layer = 0; idx_layer < layers.size(); idx_layer += 2) { | ||||
|         coordf_t lo  = layers[idx_layer]; | ||||
| 		coordf_t hi  = layers[idx_layer + 1]; | ||||
|         coordf_t mid = 0.5f * (lo + hi); | ||||
| 		assert(mid <= slicing_params.object_print_z_height()); | ||||
| 		coordf_t h = hi - lo; | ||||
| 		hi = std::min(hi, slicing_params.object_print_z_height()); | ||||
|         int cell_first = clamp(0, ncells-1, int(ceil(lo * z_to_cell))); | ||||
|         int cell_last  = clamp(0, ncells-1, int(floor(hi * z_to_cell))); | ||||
|         for (int cell = cell_first; cell <= cell_last; ++ cell) { | ||||
|             coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale; | ||||
|             int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); | ||||
|             int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); | ||||
| 			coordf_t t = idxf - coordf_t(idx1); | ||||
|             const Point3 &color1 = palette_raw[idx1]; | ||||
|             const Point3 &color2 = palette_raw[idx2]; | ||||
| 
 | ||||
|             coordf_t z = cell_to_z * coordf_t(cell); | ||||
| 			assert(z >= lo && z <= hi); | ||||
|             // Intensity profile to visualize the layers.
 | ||||
|             coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h); | ||||
| 
 | ||||
|             // Color mapping from layer height to RGB.
 | ||||
|             Pointf3 color( | ||||
|                 intensity * lerp(coordf_t(color1.x), coordf_t(color2.x), t),  | ||||
|                 intensity * lerp(coordf_t(color1.y), coordf_t(color2.y), t), | ||||
|                 intensity * lerp(coordf_t(color1.z), coordf_t(color2.z), t)); | ||||
| 
 | ||||
|             int row = cell / (cols - 1); | ||||
|             int col = cell - row * (cols - 1); | ||||
| 			assert(row >= 0 && row < rows); | ||||
| 			assert(col >= 0 && col < cols); | ||||
|             unsigned char *ptr = (unsigned char*)data + (row * cols + col) * 4; | ||||
|             ptr[0] = clamp<int>(0, 255, int(floor(color.x + 0.5))); | ||||
|             ptr[1] = clamp<int>(0, 255, int(floor(color.y + 0.5))); | ||||
|             ptr[2] = clamp<int>(0, 255, int(floor(color.z + 0.5))); | ||||
|             ptr[3] = 255; | ||||
|             if (col == 0 && row > 0) { | ||||
|                 // Duplicate the first value in a row as a last value of the preceding row.
 | ||||
|                 ptr[-4] = ptr[0]; | ||||
|                 ptr[-3] = ptr[1]; | ||||
|                 ptr[-2] = ptr[2]; | ||||
|                 ptr[-1] = ptr[3]; | ||||
|             } | ||||
|         } | ||||
|         if (level_of_detail_2nd_level) { | ||||
|             cell_first = clamp(0, ncells1-1, int(ceil(lo * z_to_cell1)));  | ||||
|             cell_last  = clamp(0, ncells1-1, int(floor(hi * z_to_cell1))); | ||||
|             for (int cell = cell_first; cell <= cell_last; ++ cell) { | ||||
|                 coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale; | ||||
|                 int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); | ||||
|                 int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); | ||||
|     			coordf_t t = idxf - coordf_t(idx1); | ||||
|                 const Point3 &color1 = palette_raw[idx1]; | ||||
|                 const Point3 &color2 = palette_raw[idx2]; | ||||
| 
 | ||||
|                 coordf_t z = cell_to_z1 * coordf_t(cell); | ||||
|                 assert(z >= lo && z <= hi); | ||||
| 
 | ||||
|                 // Color mapping from layer height to RGB.
 | ||||
|                 Pointf3 color( | ||||
|                     lerp(coordf_t(color1.x), coordf_t(color2.x), t),  | ||||
|                     lerp(coordf_t(color1.y), coordf_t(color2.y), t), | ||||
|                     lerp(coordf_t(color1.z), coordf_t(color2.z), t)); | ||||
| 
 | ||||
|                 int row = cell / (cols1 - 1); | ||||
|                 int col = cell - row * (cols1 - 1); | ||||
|     			assert(row >= 0 && row < rows/2); | ||||
|     			assert(col >= 0 && col < cols/2); | ||||
|                 unsigned char *ptr = data1 + (row * cols1 + col) * 4; | ||||
|                 ptr[0] = clamp<int>(0, 255, int(floor(color.x + 0.5))); | ||||
|                 ptr[1] = clamp<int>(0, 255, int(floor(color.y + 0.5))); | ||||
|                 ptr[2] = clamp<int>(0, 255, int(floor(color.z + 0.5))); | ||||
|                 ptr[3] = 255; | ||||
|                 if (col == 0 && row > 0) { | ||||
|                     // Duplicate the first value in a row as a last value of the preceding row.
 | ||||
|                     ptr[-4] = ptr[0]; | ||||
|                     ptr[-3] = ptr[1]; | ||||
|                     ptr[-2] = ptr[2]; | ||||
|                     ptr[-1] = ptr[3]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Returns number of cells of the 0th LOD level.
 | ||||
|     return ncells; | ||||
| } | ||||
| 
 | ||||
| }; // namespace Slic3r
 | ||||
							
								
								
									
										138
									
								
								xs/src/libslic3r/Slicing.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								xs/src/libslic3r/Slicing.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,138 @@ | |||
| // Based on implementation by @platsch
 | ||||
| 
 | ||||
| #ifndef slic3r_Slicing_hpp_ | ||||
| #define slic3r_Slicing_hpp_ | ||||
| 
 | ||||
| #include <set> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "libslic3r.h" | ||||
| namespace Slic3r | ||||
| { | ||||
| 
 | ||||
| class PrintConfig; | ||||
| class PrintObjectConfig; | ||||
| class ModelVolume; | ||||
| typedef std::vector<ModelVolume*> ModelVolumePtrs; | ||||
| 
 | ||||
| // Parameters to guide object slicing and support generation.
 | ||||
| // The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow
 | ||||
| // (using a normal flow over a soluble support, using a bridging flow over a non-soluble support).
 | ||||
| struct SlicingParameters | ||||
| { | ||||
| 	SlicingParameters() { memset(this, 0, sizeof(SlicingParameters)); } | ||||
| 
 | ||||
|     static SlicingParameters create_from_config( | ||||
|         const PrintConfig       &print_config,  | ||||
|         const PrintObjectConfig &object_config, | ||||
|         coordf_t                 object_height, | ||||
|         const std::set<size_t>  &object_extruders); | ||||
| 
 | ||||
|     // Has any raft layers?
 | ||||
|     bool        has_raft() const { return raft_layers() > 0; } | ||||
|     size_t      raft_layers() const { return base_raft_layers + interface_raft_layers; } | ||||
| 
 | ||||
|     // Is the 1st object layer height fixed, or could it be varied?
 | ||||
|     bool        first_object_layer_height_fixed()  const { return ! has_raft() || first_object_layer_bridging; } | ||||
| 
 | ||||
|     // Height of the object to be printed. This value does not contain the raft height.
 | ||||
|     coordf_t    object_print_z_height() const { return object_print_z_max - object_print_z_min; } | ||||
| 
 | ||||
|     // Number of raft layers.
 | ||||
|     size_t      base_raft_layers; | ||||
|     // Number of interface layers including the contact layer.
 | ||||
|     size_t      interface_raft_layers; | ||||
| 
 | ||||
|     // Layer heights of the raft (base, interface and a contact layer).
 | ||||
|     coordf_t    base_raft_layer_height; | ||||
|     coordf_t    interface_raft_layer_height; | ||||
|     coordf_t    contact_raft_layer_height; | ||||
|     bool        contact_raft_layer_height_bridging; | ||||
| 
 | ||||
| 	// The regular layer height, applied for all but the first layer, if not overridden by layer ranges
 | ||||
| 	// or by the variable layer thickness table.
 | ||||
|     coordf_t    layer_height; | ||||
| 
 | ||||
|     // First layer height of the print, this may be used for the first layer of the raft
 | ||||
|     // or for the first layer of the print.
 | ||||
|     coordf_t    first_print_layer_height; | ||||
| 
 | ||||
|     // Thickness of the first layer. This is either the first print layer thickness if printed without a raft,
 | ||||
|     // or a bridging flow thickness if printed over a non-soluble raft,
 | ||||
|     // or a normal layer height if printed over a soluble raft.
 | ||||
|     coordf_t    first_object_layer_height; | ||||
| 
 | ||||
|     // If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow.
 | ||||
|     bool 		first_object_layer_bridging; | ||||
| 
 | ||||
|     // Soluble interface? (PLA soluble in water, HIPS soluble in lemonen)
 | ||||
|     // otherwise the interface must be broken off.
 | ||||
|     bool        soluble_interface; | ||||
|     // Gap when placing object over raft.
 | ||||
|     coordf_t    gap_raft_object; | ||||
|     // Gap when placing support over object.
 | ||||
|     coordf_t    gap_object_support; | ||||
|     // Gap when placing object over support.
 | ||||
|     coordf_t    gap_support_object; | ||||
| 
 | ||||
|     // Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm,
 | ||||
|     // or by an interactive layer height editor.
 | ||||
|     coordf_t    min_layer_height; | ||||
|     coordf_t    max_layer_height; | ||||
| 
 | ||||
|     // Bottom and top of the printed object.
 | ||||
|     // If printed without a raft, object_print_z_min = 0 and object_print_z_max = object height.
 | ||||
|     // Otherwise object_print_z_min is equal to the raft height.
 | ||||
|     coordf_t    raft_base_top_z; | ||||
|     coordf_t    raft_interface_top_z; | ||||
|     coordf_t    raft_contact_top_z; | ||||
|     // In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer.
 | ||||
|     coordf_t 	object_print_z_min; | ||||
|     coordf_t 	object_print_z_max; | ||||
| }; | ||||
| 
 | ||||
| typedef std::pair<coordf_t,coordf_t> t_layer_height_range; | ||||
| typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges; | ||||
| 
 | ||||
| extern std::vector<coordf_t> layer_height_profile_from_ranges( | ||||
|     const SlicingParameters     &slicing_params, | ||||
|     const t_layer_height_ranges &layer_height_ranges); | ||||
| 
 | ||||
| extern std::vector<coordf_t> layer_height_profile_adaptive( | ||||
|     const SlicingParameters     &slicing_params, | ||||
|     const t_layer_height_ranges &layer_height_ranges, | ||||
|     const ModelVolumePtrs       &volumes); | ||||
| 
 | ||||
| 
 | ||||
| enum LayerHeightEditActionType { | ||||
|     LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0, | ||||
|     LAYER_HEIGHT_EDIT_ACTION_DECREASE = 1, | ||||
|     LAYER_HEIGHT_EDIT_ACTION_REDUCE   = 2, | ||||
|     LAYER_HEIGHT_EDIT_ACTION_SMOOTH   = 3 | ||||
| }; | ||||
| 
 | ||||
| extern void adjust_layer_height_profile( | ||||
|     const SlicingParameters     &slicing_params, | ||||
|     std::vector<coordf_t>       &layer_height_profile, | ||||
|     coordf_t                     z, | ||||
|     coordf_t                     layer_thickness_delta,  | ||||
|     coordf_t                     band_width, | ||||
|     LayerHeightEditActionType    action); | ||||
| 
 | ||||
| // Produce object layers as pairs of low / high layer boundaries, stored into a linear vector.
 | ||||
| // The object layers are based at z=0, ignoring the raft layers.
 | ||||
| extern std::vector<coordf_t> generate_object_layers( | ||||
|     const SlicingParameters     &slicing_params, | ||||
|     const std::vector<coordf_t> &layer_height_profile); | ||||
| 
 | ||||
| // Produce a 1D texture packed into a 2D texture describing in the RGBA format
 | ||||
| // the planned object layers.
 | ||||
| // Returns number of cells used by the texture of the 0th LOD level.
 | ||||
| extern int generate_layer_height_texture( | ||||
|     const SlicingParameters     &slicing_params, | ||||
|     const std::vector<coordf_t> &layers, | ||||
|     void *data, int rows, int cols, bool level_of_detail_2nd_level); | ||||
| 
 | ||||
| }; // namespace Slic3r
 | ||||
| 
 | ||||
| #endif /* slic3r_Slicing_hpp_ */ | ||||
							
								
								
									
										140
									
								
								xs/src/libslic3r/SlicingAdaptive.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								xs/src/libslic3r/SlicingAdaptive.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,140 @@ | |||
| #include "libslic3r.h" | ||||
| #include "TriangleMesh.hpp" | ||||
| #include "SlicingAdaptive.hpp" | ||||
| 
 | ||||
| namespace Slic3r | ||||
| { | ||||
| 
 | ||||
| void SlicingAdaptive::clear() | ||||
| { | ||||
| 	m_meshes.clear(); | ||||
| 	m_faces.clear(); | ||||
| 	m_face_normal_z.clear(); | ||||
| } | ||||
| 
 | ||||
| std::pair<float, float> face_z_span(const stl_facet *f) | ||||
| { | ||||
| 	return std::pair<float, float>( | ||||
| 		std::min(std::min(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z), | ||||
| 		std::max(std::max(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z)); | ||||
| } | ||||
| 
 | ||||
| void SlicingAdaptive::prepare() | ||||
| { | ||||
| 	// 1) Collect faces of all meshes.
 | ||||
| 	int nfaces_total = 0; | ||||
| 	for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) | ||||
| 		nfaces_total += (*it_mesh)->stl.stats.number_of_facets; | ||||
| 	m_faces.reserve(nfaces_total); | ||||
| 	for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) | ||||
| 		for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i) | ||||
| 			m_faces.push_back((*it_mesh)->stl.facet_start + i); | ||||
| 
 | ||||
| 	// 2) Sort faces lexicographically by their Z span.
 | ||||
| 	std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { | ||||
| 		std::pair<float, float> span1 = face_z_span(f1); | ||||
| 		std::pair<float, float> span2 = face_z_span(f2); | ||||
| 		return span1 < span2; | ||||
| 	}); | ||||
| 
 | ||||
| 	// 3) Generate Z components of the facet normals.
 | ||||
| 	m_face_normal_z.assign(m_faces.size(), 0.f); | ||||
|     for (size_t iface = 0; iface < m_faces.size(); ++ iface) | ||||
|     	m_face_normal_z[iface] = m_faces[iface]->normal.z; | ||||
| } | ||||
| 
 | ||||
| float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet) | ||||
| { | ||||
| 	float height = m_slicing_params.max_layer_height; | ||||
| 	bool first_hit = false; | ||||
| 	 | ||||
| 	// find all facets intersecting the slice-layer
 | ||||
| 	int ordered_id = current_facet; | ||||
| 	for (; ordered_id < int(m_faces.size()); ++ ordered_id) { | ||||
| 		std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]); | ||||
| 		// facet's minimum is higher than slice_z -> end loop
 | ||||
| 		if (zspan.first >= z) | ||||
| 			break; | ||||
| 		// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
 | ||||
| 		if (zspan.second > z) { | ||||
| 			// first event?
 | ||||
| 			if (! first_hit) { | ||||
| 				first_hit = true; | ||||
| 				current_facet = ordered_id; | ||||
| 			} | ||||
| 			// skip touching facets which could otherwise cause small cusp values
 | ||||
| 			if (zspan.second <= z + EPSILON) | ||||
| 				continue; | ||||
| 			// compute cusp-height for this facet and store minimum of all heights
 | ||||
| 			float normal_z = m_face_normal_z[ordered_id]; | ||||
| 			height = std::min(height, (normal_z == 0.f) ? 9999.f : std::abs(cusp_value / normal_z)); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// lower height limit due to printer capabilities
 | ||||
| 	height = std::max(height, float(m_slicing_params.min_layer_height)); | ||||
| 
 | ||||
| 	// check for sloped facets inside the determined layer and correct height if necessary
 | ||||
| 	if (height > m_slicing_params.min_layer_height) { | ||||
| 		for (; ordered_id < int(m_faces.size()); ++ ordered_id) { | ||||
| 			std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]); | ||||
| 			// facet's minimum is higher than slice_z + height -> end loop
 | ||||
| 			if (zspan.first >= z + height) | ||||
| 				break; | ||||
| 
 | ||||
| 			// skip touching facets which could otherwise cause small cusp values
 | ||||
| 			if (zspan.second <= z + EPSILON) | ||||
| 				continue; | ||||
| 
 | ||||
| 			// Compute cusp-height for this facet and check against height.
 | ||||
| 			float normal_z = m_face_normal_z[ordered_id]; | ||||
| 			float cusp = (normal_z == 0) ? 9999 : abs(cusp_value / normal_z); | ||||
| 			 | ||||
| 			float z_diff = zspan.first - z; | ||||
| 
 | ||||
| 			// handle horizontal facets
 | ||||
| 			if (m_face_normal_z[ordered_id] > 0.999) { | ||||
| 				// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
 | ||||
| 				height = z_diff; | ||||
| 				// Slic3r::debugf "to %f due to near horizontal facet\n", $height;
 | ||||
| 			} else if (cusp > z_diff) { | ||||
| 				if (cusp < height) { | ||||
| 					// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
 | ||||
| 					height = cusp; | ||||
| 					// Slic3r::debugf "to %f due to new cusp height\n", $height;
 | ||||
| 				} | ||||
| 			} else { | ||||
| 				// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
 | ||||
| 				height = z_diff; | ||||
| 				// Slic3r::debugf "to z-diff: %f\n", $height;
 | ||||
| 			} | ||||
| 		} | ||||
| 		// lower height limit due to printer capabilities again
 | ||||
| 		height = std::max(height, float(m_slicing_params.min_layer_height)); | ||||
| 	} | ||||
| 	 | ||||
| //	Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height;	
 | ||||
| 	return height;  | ||||
| } | ||||
| 
 | ||||
| // Returns the distance to the next horizontal facet in Z-dir 
 | ||||
| // to consider horizontal object features in slice thickness
 | ||||
| float SlicingAdaptive::horizontal_facet_distance(float z) | ||||
| { | ||||
| 	for (size_t i = 0; i < m_faces.size(); ++ i) { | ||||
| 		std::pair<float, float> zspan = face_z_span(m_faces[i]); | ||||
| 		// facet's minimum is higher than max forward distance -> end loop
 | ||||
| 		if (zspan.first > z + m_slicing_params.max_layer_height) | ||||
| 			break; | ||||
| 		// min_z == max_z -> horizontal facet
 | ||||
| 		if (zspan.first > z && zspan.first == zspan.second) | ||||
| 			return zspan.first - z; | ||||
| 	} | ||||
| 	 | ||||
| 	// objects maximum?
 | ||||
| 	return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ?  | ||||
| 		std::max<float>(m_slicing_params.object_print_z_height() - z, 0.f) : | ||||
| 		m_slicing_params.max_layer_height; | ||||
| } | ||||
| 
 | ||||
| }; // namespace Slic3r
 | ||||
							
								
								
									
										36
									
								
								xs/src/libslic3r/SlicingAdaptive.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								xs/src/libslic3r/SlicingAdaptive.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | |||
| // Based on implementation by @platsch
 | ||||
| 
 | ||||
| #ifndef slic3r_SlicingAdaptive_hpp_ | ||||
| #define slic3r_SlicingAdaptive_hpp_ | ||||
| 
 | ||||
| #include "Slicing.hpp" | ||||
| #include "admesh/stl.h" | ||||
| 
 | ||||
| namespace Slic3r | ||||
| { | ||||
| 
 | ||||
| class TriangleMesh; | ||||
| 
 | ||||
| class SlicingAdaptive | ||||
| { | ||||
| public: | ||||
| 	void clear(); | ||||
| 	void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } | ||||
| 	void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); } | ||||
| 	void prepare(); | ||||
| 	float cusp_height(float z, float cusp_value, int ¤t_facet); | ||||
| 	float horizontal_facet_distance(float z); | ||||
| 
 | ||||
| protected: | ||||
| 	SlicingParameters 					m_slicing_params; | ||||
| 
 | ||||
| 	std::vector<const TriangleMesh*>	m_meshes; | ||||
| 	// Collected faces of all meshes, sorted by raising Z of the bottom most face.
 | ||||
| 	std::vector<const stl_facet*>		m_faces; | ||||
| 	// Z component of face normals, normalized.
 | ||||
| 	std::vector<float>					m_face_normal_z; | ||||
| }; | ||||
| 
 | ||||
| }; // namespace Slic3r
 | ||||
| 
 | ||||
| #endif /* slic3r_SlicingAdaptive_hpp_ */ | ||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -3,6 +3,7 @@ | |||
| 
 | ||||
| #include "Flow.hpp" | ||||
| #include "PrintConfig.hpp" | ||||
| #include "Slicing.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -20,19 +21,33 @@ class PrintObjectConfig; | |||
| class PrintObjectSupportMaterial | ||||
| { | ||||
| public: | ||||
| 	// Support layer type to be used by MyLayer. This type carries a much more detailed information
 | ||||
| 	// about the support layer type than the final support layers stored in a PrintObject.
 | ||||
| 	enum SupporLayerType { | ||||
| 		sltUnknown = 0, | ||||
| 		sltRaft, | ||||
| 		stlFirstLayer, | ||||
| 		// Ratft base layer, to be printed with the support material.
 | ||||
| 		sltRaftBase, | ||||
| 		// Raft interface layer, to be printed with the support interface material. 
 | ||||
| 		sltRaftInterface, | ||||
| 		// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
 | ||||
| 		sltBottomContact, | ||||
| 		// Dense interface layer, to be printed with the support interface material.
 | ||||
| 		// This layer is separated from an object by an sltBottomContact layer.
 | ||||
| 		sltBottomInterface, | ||||
| 		// Sparse base support layer, to be printed with a support material.
 | ||||
| 		sltBase, | ||||
| 		// Dense interface layer, to be printed with the support interface material.
 | ||||
| 		// This layer is separated from an object with sltTopContact layer.
 | ||||
| 		sltTopInterface, | ||||
| 		// Top contact layer directly supporting an overhang. To be printed with a support interface material.
 | ||||
| 		sltTopContact, | ||||
| 		// Some undecided type yet. It will turn into stlBase first, then it may turn into stlBottomInterface or stlTopInterface.
 | ||||
| 		stlIntermediate, | ||||
| 		// Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface.
 | ||||
| 		sltIntermediate, | ||||
| 	}; | ||||
| 
 | ||||
| 	// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
 | ||||
| 	// information about the support layer than the layers stored in the PrintObject, mainly
 | ||||
| 	// the MyLayer is aware of the bridging flow and the interface gaps between the object and the support.
 | ||||
| 	class MyLayer | ||||
| 	{ | ||||
| 	public: | ||||
|  | @ -57,6 +72,7 @@ public: | |||
| 			return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging; | ||||
| 		} | ||||
| 
 | ||||
| 		// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
 | ||||
| 		bool operator<(const MyLayer &layer2) const { | ||||
| 			if (print_z < layer2.print_z) { | ||||
| 				return true; | ||||
|  | @ -72,12 +88,12 @@ public: | |||
| 		} | ||||
| 
 | ||||
| 		SupporLayerType layer_type; | ||||
| 		// Z used for printing in unscaled coordinates
 | ||||
| 		// Z used for printing, in unscaled coordinates.
 | ||||
| 		coordf_t print_z; | ||||
| 		// Bottom height of this layer. For soluble layers, bottom_z + height = print_z,
 | ||||
| 		// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
 | ||||
| 		// otherwise bottom_z + gap + height = print_z.
 | ||||
| 		coordf_t bottom_z; | ||||
| 		// layer height in unscaled coordinates
 | ||||
| 		// Layer height in unscaled coordinates.
 | ||||
|     	coordf_t height; | ||||
|     	// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
 | ||||
|     	// If this is not a contact layer, it will be set to size_t(-1).
 | ||||
|  | @ -91,63 +107,31 @@ public: | |||
|     	// Polygons to be filled by the support pattern.
 | ||||
|     	Polygons polygons; | ||||
|     	// Currently for the contact layers only: Overhangs are stored here.
 | ||||
|     	// MyLayer owns the aux_polygons, they are freed by the destructor.
 | ||||
|     	Polygons *aux_polygons; | ||||
| 	}; | ||||
| 
 | ||||
| 	struct LayerExtreme | ||||
| 	{ | ||||
| 		LayerExtreme(MyLayer *alayer, bool ais_top) : layer(alayer), is_top(ais_top) {} | ||||
| 		MyLayer 	*layer; | ||||
| 		// top or bottom extreme
 | ||||
| 		bool   		 is_top; | ||||
| 
 | ||||
| 		coordf_t	z() const { return is_top ? layer->print_z : layer->print_z - layer->height; } | ||||
| 
 | ||||
| 		bool operator<(const LayerExtreme &other) const { return z() < other.z(); } | ||||
| 	}; | ||||
| 
 | ||||
| /*
 | ||||
| 	struct LayerPrintZ_Hash { | ||||
| 		size_t operator()(const MyLayer &layer) const {  | ||||
| 			return std::hash<double>()(layer.print_z)^std::hash<double>()(layer.height)^size_t(layer.bridging); | ||||
| 		} | ||||
| 	}; | ||||
| */ | ||||
| 
 | ||||
| 	typedef std::vector<MyLayer*> 				MyLayersPtr; | ||||
| 	// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
 | ||||
| 	// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future, 
 | ||||
| 	// which would allocate layers by multiple chunks.
 | ||||
| 	typedef std::deque<MyLayer> 				MyLayerStorage; | ||||
| 	typedef std::vector<MyLayer*> 				MyLayersPtr; | ||||
| 
 | ||||
| public: | ||||
| 	PrintObjectSupportMaterial(const PrintObject *object); | ||||
| 	PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params); | ||||
| 
 | ||||
| 	// Height of the 1st layer is user configured as it is important for the print
 | ||||
| 	// to stick to he print bed.
 | ||||
| 	coordf_t	first_layer_height() 		const { return m_object_config->first_layer_height.value; } | ||||
| 
 | ||||
| 	// Is raft enabled?
 | ||||
| 	bool 		has_raft() 					const { return m_has_raft; } | ||||
| 	bool 		has_raft() 					const { return m_slicing_params.has_raft(); } | ||||
| 	// Has any support?
 | ||||
| 	bool 		has_support()				const { return m_object_config->support_material.value; } | ||||
| 	bool 		build_plate_only() 			const { return this->has_support() && m_object_config->support_material_buildplate_only.value; } | ||||
| 
 | ||||
| 	// How many raft layers are there below the 1st object layer?
 | ||||
| 	// The 1st object layer_id will be offsetted by this number.
 | ||||
| 	size_t 		num_raft_layers() 			const { return m_object_config->raft_layers.value; } | ||||
| 	// num_raft_layers() == num_raft_base_layers() + num_raft_interface_layers() + num_raft_contact_layers().
 | ||||
| 	size_t 		num_raft_base_layers() 		const { return m_num_base_raft_layers; } | ||||
| 	size_t 		num_raft_interface_layers() const { return m_num_interface_raft_layers; } | ||||
| 	size_t 		num_raft_contact_layers() 	const { return m_num_contact_raft_layers; } | ||||
| 
 | ||||
| 	coordf_t 	raft_height() 			    const { return m_raft_height; } | ||||
| 	coordf_t    raft_base_height() 			const { return m_raft_base_height; } | ||||
| 	coordf_t	raft_interface_height() 	const { return m_raft_interface_height; } | ||||
| 	coordf_t	raft_contact_height() 		const { return m_raft_contact_height; } | ||||
| 	bool 		raft_bridging() 			const { return m_raft_contact_layer_bridging; } | ||||
| 
 | ||||
| 	// 1st layer of the object will be printed depeding on the raft settings.
 | ||||
| 	coordf_t 	first_object_layer_print_z() 	const { return m_object_1st_layer_print_z; } | ||||
| 	coordf_t 	first_object_layer_height() 	const { return m_object_1st_layer_height; } | ||||
| 	coordf_t 	first_object_layer_gap() 		const { return m_object_1st_layer_gap; } | ||||
| 	bool 		first_object_layer_bridging() 	const { return m_object_1st_layer_bridging; } | ||||
| 	bool 		synchronize_layers()		const { return m_object_config->support_material_synchronize_layers.value; } | ||||
| 	bool 		has_contact_loops() 		const { return m_object_config->support_material_interface_contact_loops.value; } | ||||
| 
 | ||||
| 	// Generate support material for the object.
 | ||||
| 	// New support layers will be added to the object,
 | ||||
|  | @ -163,7 +147,9 @@ private: | |||
| 	// Generate bottom contact layers supporting the top contact layers.
 | ||||
| 	// For a soluble interface material synchronize the layer heights with the object, 
 | ||||
| 	// otherwise set the layer height to a bridging flow of a support interface nozzle.
 | ||||
| 	MyLayersPtr bottom_contact_layers(const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const; | ||||
| 	MyLayersPtr bottom_contact_layers_and_layer_support_areas( | ||||
| 		const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage, | ||||
| 		std::vector<Polygons> &layer_support_areas) const; | ||||
| 
 | ||||
| 	// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
 | ||||
| 	void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const; | ||||
|  | @ -176,17 +162,23 @@ private: | |||
| 	    MyLayerStorage	 	&layer_storage, | ||||
| 	    const coordf_t       max_object_layer_height) const; | ||||
| 
 | ||||
| 	// Fill in the base layers with polygons.
 | ||||
| 	void generate_base_layers( | ||||
| 	    const PrintObject   &object, | ||||
| 	    const MyLayersPtr   &bottom_contacts, | ||||
| 	    const MyLayersPtr   &top_contacts, | ||||
| 	    MyLayersPtr         &intermediate_layers) const; | ||||
| 	    MyLayersPtr         &intermediate_layers, | ||||
| 	    std::vector<Polygons> &layer_support_areas) const; | ||||
| 
 | ||||
|     Polygons generate_raft_base( | ||||
| 	// Generate raft layers, also expand the 1st support layer
 | ||||
| 	// in case there is no raft layer to improve support adhesion.
 | ||||
|     MyLayersPtr generate_raft_base( | ||||
| 	    const PrintObject   &object, | ||||
| 	    const MyLayersPtr   &bottom_contacts, | ||||
| 	    MyLayersPtr         &intermediate_layers) const; | ||||
| 	    const MyLayersPtr   &top_contacts, | ||||
| 	    MyLayersPtr         &intermediate_layers, | ||||
| 	    MyLayerStorage	 	&layer_storage) const; | ||||
| 
 | ||||
|     // Turn some of the base layers into interface layers.
 | ||||
| 	MyLayersPtr generate_interface_layers( | ||||
| 	    const PrintObject   &object, | ||||
| 	    const MyLayersPtr   &bottom_contacts, | ||||
|  | @ -194,6 +186,15 @@ private: | |||
| 	    MyLayersPtr         &intermediate_layers, | ||||
| 	    MyLayerStorage      &layer_storage) const; | ||||
| 
 | ||||
| 	// Trim support layers by an object to leave a defined gap between
 | ||||
| 	// the support volume and the object.
 | ||||
| 	void trim_support_layers_by_object( | ||||
| 	    const PrintObject   &object, | ||||
| 	    MyLayersPtr         &support_layers, | ||||
| 	    const coordf_t       gap_extra_above, | ||||
| 	    const coordf_t       gap_extra_below, | ||||
| 	    const coordf_t       gap_xy) const; | ||||
| 
 | ||||
| /*
 | ||||
| 	void generate_pillars_shape(); | ||||
| 	void clip_with_shape(); | ||||
|  | @ -202,59 +203,28 @@ private: | |||
| 	// Produce the actual G-code.
 | ||||
| 	void generate_toolpaths( | ||||
|         const PrintObject   &object, | ||||
|         const Polygons 		&raft, | ||||
|         const MyLayersPtr 	&raft_layers, | ||||
|         const MyLayersPtr   &bottom_contacts, | ||||
|         const MyLayersPtr   &top_contacts, | ||||
|         const MyLayersPtr   &intermediate_layers, | ||||
|         const MyLayersPtr   &interface_layers) const; | ||||
| 
 | ||||
| 	// Following objects are not owned by SupportMaterial class.
 | ||||
| 	const PrintObject 		*m_object; | ||||
| 	const PrintConfig 		*m_print_config; | ||||
| 	const PrintObjectConfig *m_object_config; | ||||
| 	// Pre-calculated parameters shared between the object slicer and the support generator,
 | ||||
| 	// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
 | ||||
| 	SlicingParameters	     m_slicing_params; | ||||
| 
 | ||||
| 	Flow 			 	 m_first_layer_flow; | ||||
| 	Flow 			 	 m_support_material_flow; | ||||
| 	Flow 			 	 m_support_material_interface_flow; | ||||
| 	bool 			 	 m_soluble_interface; | ||||
| 
 | ||||
| 	Flow 				 m_support_material_raft_base_flow; | ||||
| 	Flow 				 m_support_material_raft_interface_flow; | ||||
| 	Flow 				 m_support_material_raft_contact_flow; | ||||
| 
 | ||||
| 	bool 				 m_has_raft; | ||||
| 	size_t 				 m_num_base_raft_layers; | ||||
| 	size_t 				 m_num_interface_raft_layers; | ||||
| 	size_t 				 m_num_contact_raft_layers; | ||||
| 	// If set, the raft contact layer is laid with round strings, which are easily detachable
 | ||||
| 	// from both the below and above layes.
 | ||||
| 	// Otherwise a normal flow is used and the strings are squashed against the layer below, 
 | ||||
| 	// creating a firm bond with the layer below and making the interface top surface flat.
 | ||||
| 	coordf_t 			 m_raft_height; | ||||
| 	coordf_t 			 m_raft_base_height; | ||||
| 	coordf_t			 m_raft_interface_height; | ||||
| 	coordf_t			 m_raft_contact_height; | ||||
| 	bool 				 m_raft_contact_layer_bridging; | ||||
| 
 | ||||
| 	coordf_t 			 m_object_1st_layer_print_z; | ||||
| 	coordf_t			 m_object_1st_layer_height; | ||||
| 	coordf_t 			 m_object_1st_layer_gap; | ||||
| 	bool 				 m_object_1st_layer_bridging; | ||||
| 
 | ||||
|     coordf_t 			 m_object_layer_height_max; | ||||
|     coordf_t 			 m_support_layer_height_min; | ||||
| 	coordf_t		 	 m_support_layer_height_max; | ||||
| 	coordf_t		 	 m_support_interface_layer_height_max; | ||||
| 
 | ||||
| 	coordf_t  			 m_gap_extra_above; | ||||
| 	coordf_t 			 m_gap_extra_below; | ||||
| 	coordf_t			 m_gap_xy; | ||||
| 
 | ||||
| 	// If enabled, the support layers will be synchronized with object layers.
 | ||||
| 	// This does not prevent the support layers to be combined.
 | ||||
| 	bool 				 m_synchronize_support_layers_with_object; | ||||
| 	// If disabled and m_synchronize_support_layers_with_object,
 | ||||
| 	// the support layers will be synchronized with the object layers exactly, no layer will be combined.
 | ||||
| 	bool 				 m_combine_support_layers; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
|  |  | |||
|  | @ -57,6 +57,7 @@ public: | |||
|     operator Polygons() const; | ||||
|     double area() const; | ||||
|     bool empty() const { return expolygon.empty(); } | ||||
|     void clear() { expolygon.clear(); } | ||||
|     bool is_solid() const; | ||||
|     bool is_external() const; | ||||
|     bool is_internal() const; | ||||
|  |  | |||
|  | @ -8,22 +8,12 @@ namespace Slic3r { | |||
| 
 | ||||
| SurfaceCollection::operator Polygons() const | ||||
| { | ||||
|     Polygons polygons; | ||||
|     for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { | ||||
|         Polygons surface_p = surface->expolygon; | ||||
|         polygons.insert(polygons.end(), surface_p.begin(), surface_p.end()); | ||||
|     } | ||||
|     return polygons; | ||||
| 	return to_polygons(surfaces); | ||||
| } | ||||
| 
 | ||||
| SurfaceCollection::operator ExPolygons() const | ||||
| { | ||||
|     ExPolygons expp; | ||||
|     expp.reserve(this->surfaces.size()); | ||||
|     for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { | ||||
|         expp.push_back(surface->expolygon); | ||||
|     } | ||||
|     return expp; | ||||
| 	return to_expolygons(surfaces); | ||||
| } | ||||
| 
 | ||||
| void | ||||
|  | @ -196,19 +186,6 @@ SurfaceCollection::remove_types(const SurfaceType *types, int ntypes) | |||
|         surfaces.erase(surfaces.begin() + j, surfaces.end()); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| SurfaceCollection::append(const SurfaceCollection &coll) | ||||
| { | ||||
|     this->surfaces.insert(this->surfaces.end(), coll.surfaces.begin(), coll.surfaces.end()); | ||||
| } | ||||
| 
 | ||||
| void  | ||||
| SurfaceCollection::append(const SurfaceType surfaceType, const Slic3r::ExPolygons &expoly) | ||||
| { | ||||
|     for (Slic3r::ExPolygons::const_iterator it = expoly.begin(); it != expoly.end(); ++ it) | ||||
|         this->surfaces.push_back(Slic3r::Surface(surfaceType, *it)); | ||||
| } | ||||
| 
 | ||||
| void SurfaceCollection::export_to_svg(const char *path, bool show_labels)  | ||||
| { | ||||
|     BoundingBox bbox; | ||||
|  |  | |||
|  | @ -28,8 +28,27 @@ class SurfaceCollection | |||
|     void remove_type(const SurfaceType type); | ||||
|     void remove_types(const SurfaceType *types, int ntypes); | ||||
|     void filter_by_type(SurfaceType type, Polygons* polygons); | ||||
|     void append(const SurfaceCollection &coll); | ||||
|     void append(const SurfaceType surfaceType, const ExPolygons &expoly); | ||||
| 
 | ||||
|     void clear() { surfaces.clear(); } | ||||
|     bool empty() const { return surfaces.empty(); } | ||||
| 
 | ||||
|     void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; } | ||||
|     void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); } | ||||
|     void set(const ExPolygons &src, SurfaceType surfaceType) { clear(); this->append(src, surfaceType); } | ||||
|     void set(const ExPolygons &src, const Surface &surfaceTempl) { clear(); this->append(src, surfaceTempl); } | ||||
|     void set(const Surfaces &src) { clear(); this->append(src); } | ||||
|     void set(ExPolygons &&src, SurfaceType surfaceType) { clear(); this->append(std::move(src), surfaceType); } | ||||
|     void set(ExPolygons &&src, const Surface &surfaceTempl) { clear(); this->append(std::move(src), surfaceTempl); } | ||||
|     void set(Surfaces &&src) { clear(); this->append(std::move(src)); } | ||||
| 
 | ||||
|     void append(const SurfaceCollection &coll) { this->append(coll.surfaces); } | ||||
|     void append(SurfaceCollection &&coll) { this->append(std::move(coll.surfaces)); } | ||||
|     void append(const ExPolygons &src, SurfaceType surfaceType) { surfaces_append(this->surfaces, src, surfaceType); } | ||||
|     void append(const ExPolygons &src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, src, surfaceTempl); } | ||||
|     void append(const Surfaces &src) { surfaces_append(this->surfaces, src); } | ||||
|     void append(ExPolygons &&src, SurfaceType surfaceType) { surfaces_append(this->surfaces, std::move(src), surfaceType); } | ||||
|     void append(ExPolygons &&src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, std::move(src), surfaceTempl); } | ||||
|     void append(Surfaces &&src) { surfaces_append(this->surfaces, std::move(src)); } | ||||
| 
 | ||||
|     // For debugging purposes:
 | ||||
|     void export_to_svg(const char *path, bool show_labels); | ||||
|  |  | |||
|  | @ -2,8 +2,8 @@ | |||
| #include "ClipperUtils.hpp" | ||||
| #include "Geometry.hpp" | ||||
| #include <cmath> | ||||
| #include <queue> | ||||
| #include <deque> | ||||
| #include <queue> | ||||
| #include <set> | ||||
| #include <vector> | ||||
| #include <map> | ||||
|  | @ -193,12 +193,17 @@ void TriangleMesh::scale(const Pointf3 &versor) | |||
| 
 | ||||
| void TriangleMesh::translate(float x, float y, float z) | ||||
| { | ||||
|     if (x == 0.f && y == 0.f && z == 0.f) | ||||
|         return; | ||||
|     stl_translate_relative(&(this->stl), x, y, z); | ||||
|     stl_invalidate_shared_vertices(&this->stl); | ||||
| } | ||||
| 
 | ||||
| void TriangleMesh::rotate(float angle, const Axis &axis) | ||||
| { | ||||
|     if (angle == 0.f) | ||||
|         return; | ||||
| 
 | ||||
|     // admesh uses degrees
 | ||||
|     angle = Slic3r::Geometry::rad2deg(angle); | ||||
|      | ||||
|  | @ -265,6 +270,8 @@ void TriangleMesh::align_to_origin() | |||
| 
 | ||||
| void TriangleMesh::rotate(double angle, Point* center) | ||||
| { | ||||
|     if (angle == 0.) | ||||
|         return; | ||||
|     this->translate(-center->x, -center->y, 0); | ||||
|     stl_rotate_z(&(this->stl), (float)angle); | ||||
|     this->translate(+center->x, +center->y, 0); | ||||
|  | @ -363,10 +370,7 @@ TriangleMesh::horizontal_projection() const | |||
|     } | ||||
|      | ||||
|     // the offset factor was tuned using groovemount.stl
 | ||||
|     offset(pp, &pp, 0.01 / SCALING_FACTOR); | ||||
|     ExPolygons retval; | ||||
|     union_(pp, &retval, true); | ||||
|     return retval; | ||||
|     return union_ex(offset(pp, 0.01 / SCALING_FACTOR), true); | ||||
| } | ||||
| 
 | ||||
| Polygon | ||||
|  | @ -403,7 +407,7 @@ TriangleMesh::require_shared_vertices() | |||
| } | ||||
| 
 | ||||
| void | ||||
| TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) | ||||
| TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const | ||||
| { | ||||
|     /*
 | ||||
|        This method gets called with a list of unscaled Z coordinates and outputs | ||||
|  | @ -427,26 +431,47 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la | |||
|          | ||||
|         At the end, we free the tables generated by analyze() as we don't  | ||||
|         need them anymore. | ||||
|         FUTURE: parallelize slice_facet() and make_loops() | ||||
|          | ||||
|         NOTE: this method accepts a vector of floats because the mesh coordinate | ||||
|         type is float. | ||||
|     */ | ||||
|      | ||||
|     std::vector<IntersectionLines> lines(z.size()); | ||||
|     { | ||||
|         boost::mutex lines_mutex; | ||||
|         parallelize<int>( | ||||
|             0, | ||||
|             this->mesh->stl.stats.number_of_facets-1, | ||||
|             boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z) | ||||
|         ); | ||||
|     } | ||||
|      | ||||
|     for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { | ||||
|         stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; | ||||
|     // v_scaled_shared could be freed here
 | ||||
|      | ||||
|     // build loops
 | ||||
|     layers->resize(z.size()); | ||||
|     parallelize<size_t>( | ||||
|         0, | ||||
|         lines.size()-1, | ||||
|         boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers) | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex,  | ||||
|     const std::vector<float> &z) const | ||||
| { | ||||
|     const stl_facet &facet = this->mesh->stl.facet_start[facet_idx]; | ||||
|      | ||||
|     // find facet extents
 | ||||
|         float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z)); | ||||
|         float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); | ||||
|     const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); | ||||
|     const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); | ||||
|      | ||||
|         #ifdef SLIC3R_TRIANGLEMESH_DEBUG | ||||
|     #ifdef SLIC3R_DEBUG | ||||
|     printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, | ||||
|             facet->vertex[0].x, facet->vertex[0].y, facet->vertex[0].z, | ||||
|             facet->vertex[1].x, facet->vertex[1].y, facet->vertex[1].z, | ||||
|             facet->vertex[2].x, facet->vertex[2].y, facet->vertex[2].z); | ||||
|         facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, | ||||
|         facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, | ||||
|         facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); | ||||
|     printf("z: min = %.2f, max = %.2f\n", min_z, max_z); | ||||
|     #endif | ||||
|      | ||||
|  | @ -454,31 +479,18 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la | |||
|     std::vector<float>::const_iterator min_layer, max_layer; | ||||
|     min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
 | ||||
|     max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
 | ||||
|         #ifdef SLIC3R_TRIANGLEMESH_DEBUG | ||||
|     #ifdef SLIC3R_DEBUG | ||||
|     printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin())); | ||||
|     #endif | ||||
|      | ||||
|     for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) { | ||||
|         std::vector<float>::size_type layer_idx = it - z.begin(); | ||||
|             this->slice_facet(*it / SCALING_FACTOR, *facet, facet_idx, min_z, max_z, &lines[layer_idx]); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     // v_scaled_shared could be freed here
 | ||||
|      | ||||
|     // build loops
 | ||||
|     layers->resize(z.size()); | ||||
|     for (std::vector<IntersectionLines>::iterator it = lines.begin(); it != lines.end(); ++it) { | ||||
|         size_t layer_idx = it - lines.begin(); | ||||
|         #ifdef SLIC3R_TRIANGLEMESH_DEBUG | ||||
|         printf("Layer " PRINTF_ZU ":\n", layer_idx); | ||||
|         #endif | ||||
|         this->make_loops(*it, &(*layers)[layer_idx]); | ||||
|         this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void | ||||
| TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) | ||||
| TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const | ||||
| { | ||||
|     std::vector<Polygons> layers_p; | ||||
|     this->slice(z, &layers_p); | ||||
|  | @ -495,7 +507,9 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* | |||
| } | ||||
| 
 | ||||
| void | ||||
| TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines) const | ||||
| TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, | ||||
|     const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines, | ||||
|     boost::mutex* lines_mutex) const | ||||
| { | ||||
|     std::vector<IntersectionPoint> points; | ||||
|     std::vector< std::vector<IntersectionPoint>::size_type > points_on_layer; | ||||
|  | @ -547,7 +561,12 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int | |||
|             line.b.y    = b->y; | ||||
|             line.a_id   = a_id; | ||||
|             line.b_id   = b_id; | ||||
|             if (lines_mutex != NULL) { | ||||
|                 boost::lock_guard<boost::mutex> l(*lines_mutex); | ||||
|                 lines->push_back(line); | ||||
|             } else { | ||||
|                 lines->push_back(line); | ||||
|             } | ||||
|              | ||||
|             found_horizontal_edge = true; | ||||
|              | ||||
|  | @ -600,13 +619,24 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int | |||
|         line.b_id       = points[0].point_id; | ||||
|         line.edge_a_id  = points[1].edge_id; | ||||
|         line.edge_b_id  = points[0].edge_id; | ||||
|         if (lines_mutex != NULL) { | ||||
|             boost::lock_guard<boost::mutex> l(*lines_mutex); | ||||
|             lines->push_back(line); | ||||
|         } else { | ||||
|             lines->push_back(line); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void | ||||
| TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) | ||||
| TriangleMeshSlicer::_make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const | ||||
| { | ||||
|     this->make_loops((*lines)[i], &(*layers)[i]); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const | ||||
| { | ||||
|     /*
 | ||||
|     SVG svg("lines.svg"); | ||||
|  | @ -707,6 +737,7 @@ TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* l | |||
|                     for (IntersectionLinePtrs::const_iterator lineptr = loop.begin(); lineptr != loop.end(); ++lineptr) { | ||||
|                         p.points.push_back((*lineptr)->a); | ||||
|                     } | ||||
|                      | ||||
|                     loops->push_back(p); | ||||
|                      | ||||
|                     #ifdef SLIC3R_TRIANGLEMESH_DEBUG | ||||
|  | @ -746,7 +777,7 @@ class _area_comp { | |||
| }; | ||||
| 
 | ||||
| void | ||||
| TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) | ||||
| TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const | ||||
| { | ||||
|     Polygons loops; | ||||
|     this->make_loops(lines, &loops); | ||||
|  | @ -780,7 +811,7 @@ TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, | |||
| } | ||||
| 
 | ||||
| void | ||||
| TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) | ||||
| TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const | ||||
| { | ||||
|     /*
 | ||||
|         Input loops are not suitable for evenodd nor nonzero fill types, as we might get | ||||
|  | @ -818,17 +849,15 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) | |||
|            of the loops, since the Orientation() function provided by Clipper | ||||
|            would do the same, thus repeating the calculation */ | ||||
|         Polygons::const_iterator loop = loops.begin() + *loop_idx; | ||||
|         if (area[*loop_idx] > +EPSILON) { | ||||
|         if (area[*loop_idx] > +EPSILON) | ||||
|             p_slices.push_back(*loop); | ||||
|         } else if (area[*loop_idx] < -EPSILON) { | ||||
|             diff(p_slices, *loop, &p_slices); | ||||
|         } | ||||
|         else if (area[*loop_idx] < -EPSILON) | ||||
|             p_slices = diff(p_slices, *loop); | ||||
|     } | ||||
| 
 | ||||
|     // perform a safety offset to merge very close facets (TODO: find test case for this)
 | ||||
|     double safety_offset = scale_(0.0499); | ||||
|     ExPolygons ex_slices; | ||||
|     offset2(p_slices, &ex_slices, +safety_offset, -safety_offset); | ||||
|     ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset); | ||||
|      | ||||
|     #ifdef SLIC3R_TRIANGLEMESH_DEBUG | ||||
|     size_t holes_count = 0; | ||||
|  | @ -840,11 +869,11 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) | |||
|     #endif | ||||
|      | ||||
|     // append to the supplied collection
 | ||||
|     slices->insert(slices->end(), ex_slices.begin(), ex_slices.end()); | ||||
|     expolygons_append(*slices, ex_slices); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) | ||||
| TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const | ||||
| { | ||||
|     Polygons pp; | ||||
|     this->make_loops(lines, &pp); | ||||
|  | @ -852,7 +881,7 @@ TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPoly | |||
| } | ||||
| 
 | ||||
| void | ||||
| TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) | ||||
| TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const | ||||
| { | ||||
|     IntersectionLines upper_lines, lower_lines; | ||||
|      | ||||
|  | @ -1004,7 +1033,6 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) | |||
|      | ||||
|     stl_get_size(&(upper->stl)); | ||||
|     stl_get_size(&(lower->stl)); | ||||
|      | ||||
| } | ||||
| 
 | ||||
| TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL) | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| #include "libslic3r.h" | ||||
| #include <admesh/stl.h> | ||||
| #include <vector> | ||||
| #include <boost/thread.hpp> | ||||
| #include "BoundingBox.hpp" | ||||
| #include "Line.hpp" | ||||
| #include "Point.hpp" | ||||
|  | @ -88,19 +89,23 @@ class TriangleMeshSlicer | |||
|     TriangleMesh* mesh; | ||||
|     TriangleMeshSlicer(TriangleMesh* _mesh); | ||||
|     ~TriangleMeshSlicer(); | ||||
|     void slice(const std::vector<float> &z, std::vector<Polygons>* layers); | ||||
|     void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers); | ||||
|     void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines) const; | ||||
|     void cut(float z, TriangleMesh* upper, TriangleMesh* lower); | ||||
|     void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const; | ||||
|     void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const; | ||||
|     void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, | ||||
|         const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines, | ||||
|         boost::mutex* lines_mutex = NULL) const; | ||||
|     void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; | ||||
|      | ||||
|     private: | ||||
|     typedef std::vector< std::vector<int> > t_facets_edges; | ||||
|     t_facets_edges facets_edges; | ||||
|     stl_vertex* v_scaled_shared; | ||||
|     void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops); | ||||
|     void make_expolygons(const Polygons &loops, ExPolygons* slices); | ||||
|     void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices); | ||||
|     void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices); | ||||
|     void _slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, const std::vector<float> &z) const; | ||||
|     void _make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const; | ||||
|     void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const; | ||||
|     void make_expolygons(const Polygons &loops, ExPolygons* slices) const; | ||||
|     void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const; | ||||
|     void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										10
									
								
								xs/src/libslic3r/Utils.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								xs/src/libslic3r/Utils.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| #ifndef slic3r_Utils_hpp_ | ||||
| #define slic3r_Utils_hpp_ | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| extern void set_logging_level(unsigned int level); | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif // slic3r_Utils_hpp_
 | ||||
|  | @ -4,10 +4,14 @@ | |||
| // this needs to be included early for MSVC (listing it in Build.PL is not enough)
 | ||||
| #include <ostream> | ||||
| #include <iostream> | ||||
| #include <math.h> | ||||
| #include <queue> | ||||
| #include <sstream> | ||||
| #include <cstdio> | ||||
| #include <stdint.h> | ||||
| #include <stdarg.h> | ||||
| #include <vector> | ||||
| #include <boost/thread.hpp> | ||||
| 
 | ||||
| #define SLIC3R_FORK_NAME "Slic3r Prusa Edition" | ||||
| #define SLIC3R_VERSION "1.31.6" | ||||
|  | @ -40,13 +44,6 @@ | |||
| typedef long coord_t; | ||||
| typedef double coordf_t; | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| enum Axis { X=0, Y, Z }; | ||||
| 
 | ||||
| } | ||||
| using namespace Slic3r; | ||||
| 
 | ||||
| /* Implementation of CONFESS("foo"): */ | ||||
| #ifdef _MSC_VER | ||||
| 	#define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) | ||||
|  | @ -91,4 +88,55 @@ inline std::string debug_out_path(const char *name, ...) | |||
| // Write slices as SVG images into out directory during the 2D processing of the slices.
 | ||||
| // #define SLIC3R_DEBUG_SLICE_PROCESSING
 | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| enum Axis { X=0, Y, Z }; | ||||
| 
 | ||||
| template <class T> | ||||
| inline void append_to(std::vector<T> &dst, const std::vector<T> &src) | ||||
| { | ||||
|     dst.insert(dst.end(), src.begin(), src.end()); | ||||
| } | ||||
| 
 | ||||
| template <class T> void | ||||
| _parallelize_do(std::queue<T>* queue, boost::mutex* queue_mutex, boost::function<void(T)> func) | ||||
| { | ||||
|     //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl;
 | ||||
|     while (true) { | ||||
|         T i; | ||||
|         { | ||||
|             boost::lock_guard<boost::mutex> l(*queue_mutex); | ||||
|             if (queue->empty()) return; | ||||
|             i = queue->front(); | ||||
|             queue->pop(); | ||||
|         } | ||||
|         //std::cout << "  Thread " << boost::this_thread::get_id() << " processing item " << i << std::endl;
 | ||||
|         func(i); | ||||
|         boost::this_thread::interruption_point(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <class T> void | ||||
| parallelize(std::queue<T> queue, boost::function<void(T)> func, | ||||
|     int threads_count = boost::thread::hardware_concurrency()) | ||||
| { | ||||
|     if (threads_count == 0) threads_count = 2; | ||||
|     boost::mutex queue_mutex; | ||||
|     boost::thread_group workers; | ||||
|     for (int i = 0; i < std::min(threads_count, int(queue.size())); ++ i) | ||||
|         workers.add_thread(new boost::thread(&_parallelize_do<T>, &queue, &queue_mutex, func)); | ||||
|     workers.join_all(); | ||||
| } | ||||
| 
 | ||||
| template <class T> void | ||||
| parallelize(T start, T end, boost::function<void(T)> func, | ||||
|     int threads_count = boost::thread::hardware_concurrency()) | ||||
| { | ||||
|     std::queue<T> queue; | ||||
|     for (T i = start; i <= end; ++i) queue.push(i); | ||||
|     parallelize(queue, func, threads_count); | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -1,3 +1,30 @@ | |||
| #include <boost/log/core.hpp> | ||||
| #include <boost/log/trivial.hpp> | ||||
| #include <boost/log/expressions.hpp> | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
| static boost::log::trivial::severity_level logSeverity = boost::log::trivial::fatal; | ||||
| 
 | ||||
| void set_logging_level(unsigned int level) | ||||
| { | ||||
|     switch (level) { | ||||
|     case 0: logSeverity = boost::log::trivial::fatal; break; | ||||
|     case 1: logSeverity = boost::log::trivial::error; break; | ||||
|     case 2: logSeverity = boost::log::trivial::warning; break; | ||||
|     case 3: logSeverity = boost::log::trivial::info; break; | ||||
|     case 4: logSeverity = boost::log::trivial::debug; break; | ||||
|     default: logSeverity = boost::log::trivial::trace; break; | ||||
|     } | ||||
| 
 | ||||
|     boost::log::core::get()->set_filter | ||||
|     ( | ||||
|         boost::log::trivial::severity >= logSeverity | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| } // namespace Slic3r
 | ||||
| 
 | ||||
| #ifdef SLIC3R_HAS_BROKEN_CROAK | ||||
| 
 | ||||
| // Some Strawberry Perl builds (mainly the latest 64bit builds) have a broken mechanism
 | ||||
|  |  | |||
|  | @ -532,8 +532,7 @@ SV* | |||
| polynode2perl(const ClipperLib::PolyNode& node) | ||||
| { | ||||
|     HV* hv = newHV(); | ||||
|     Slic3r::Polygon p; | ||||
|     ClipperPath_to_Slic3rMultiPoint(node.Contour, &p); | ||||
|     Slic3r::Polygon p = ClipperPath_to_Slic3rPolygon(node.Contour); | ||||
|     if (node.IsHole()) { | ||||
|         (void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) ); | ||||
|     } else { | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ | |||
| #include <ostream> | ||||
| #include <iostream> | ||||
| #include <sstream> | ||||
| #include <libslic3r.h> | ||||
| 
 | ||||
| #ifdef SLIC3RXS | ||||
| extern "C" { | ||||
|  | @ -42,15 +43,18 @@ extern "C" { | |||
| #undef do_close | ||||
| #undef bind | ||||
| #undef seed | ||||
| #undef push | ||||
| #undef pop | ||||
| #ifdef _MSC_VER | ||||
|     // Undef some of the macros set by Perl <xsinit.h>, which cause compilation errors on Win32
 | ||||
|     #undef send | ||||
|     #undef connect | ||||
|     #undef seek | ||||
|     #undef send | ||||
|     #undef write | ||||
| #endif /* _MSC_VER */ | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #include <libslic3r.h> | ||||
| #include <ClipperUtils.hpp> | ||||
| #include <Config.hpp> | ||||
| #include <ExPolygon.hpp> | ||||
|  | @ -163,4 +167,6 @@ SV* polynode2perl(const ClipperLib::PolyNode& node); | |||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| using namespace Slic3r; | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ use warnings; | |||
| 
 | ||||
| use List::Util qw(sum); | ||||
| use Slic3r::XS; | ||||
| use Test::More tests => 23; | ||||
| use Test::More tests => 16; | ||||
| 
 | ||||
| my $square = Slic3r::Polygon->new(  # ccw | ||||
|     [200, 100], | ||||
|  | @ -121,41 +121,6 @@ if (0) {  # Clipper does not preserve polyline orientation | |||
|     is_deeply $result->[0]->pp, [[200,150], [100,150]], 'clipped line orientation is preserved'; | ||||
| } | ||||
| 
 | ||||
| if (0) {  # Clipper does not preserve polyline orientation | ||||
|     my $result = Slic3r::Geometry::Clipper::intersection_ppl([$hole_in_square], [$square]); | ||||
|     is_deeply $result->[0]->pp, $hole_in_square->split_at_first_point->pp, | ||||
|         'intersection_ppl - clipping cw polygon as polyline preserves winding order'; | ||||
| } | ||||
| 
 | ||||
| { | ||||
|     my $square2 = $square->clone; | ||||
|     $square2->translate(50,50); | ||||
|     { | ||||
|         my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]); | ||||
|         is scalar(@$result), 1, 'intersection_ppl - result contains a single line'; | ||||
|         is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points'; | ||||
|         # Clipper does not preserve polyline orientation so we only check the middle point | ||||
|         ###ok $result->[0][0]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order'; | ||||
|         ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order'; | ||||
|         ###ok $result->[0][2]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order'; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| { | ||||
|     my $square2 = $square->clone; | ||||
|     $square2->reverse; | ||||
|     $square2->translate(50,50); | ||||
|     { | ||||
|         my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]); | ||||
|         is scalar(@$result), 1, 'intersection_ppl - result contains a single line'; | ||||
|         is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points'; | ||||
|         # Clipper does not preserve polyline orientation so we only check the middle point | ||||
|         ###ok $result->[0][0]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order'; | ||||
|         ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order'; | ||||
|         ###ok $result->[0][2]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order'; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| { | ||||
|     # Clipper bug #96 (our issue #2028) | ||||
|     my $subject = Slic3r::Polyline->new( | ||||
|  | @ -168,17 +133,6 @@ if (0) {  # Clipper does not preserve polyline orientation | |||
|     is scalar(@$result), 1, 'intersection_pl - result is not empty'; | ||||
| } | ||||
| 
 | ||||
| { | ||||
|     my $subject = Slic3r::Polygon->new( | ||||
|         [44730000,31936670],[55270000,31936670],[55270000,25270000],[74730000,25270000],[74730000,44730000],[68063296,44730000],[68063296,55270000],[74730000,55270000],[74730000,74730000],[55270000,74730000],[55270000,68063296],[44730000,68063296],[44730000,74730000],[25270000,74730000],[25270000,55270000],[31936670,55270000],[31936670,44730000],[25270000,44730000],[25270000,25270000],[44730000,25270000] | ||||
|     ); | ||||
|     my $clip = [ | ||||
|         Slic3r::Polygon->new([75200000,45200000],[54800000,45200000],[54800000,24800000],[75200000,24800000]), | ||||
|     ]; | ||||
|     my $result = Slic3r::Geometry::Clipper::intersection_ppl([$subject], $clip); | ||||
|     is scalar(@$result), 1, 'intersection_ppl - result is not empty'; | ||||
| } | ||||
| 
 | ||||
| { | ||||
|     # Clipper bug #122 | ||||
|     my $subject = [ | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ | |||
|     long y_min() %code{% RETVAL = THIS->min.y; %}; | ||||
|     long y_max() %code{% RETVAL = THIS->max.y; %}; | ||||
|     std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld;%ld,%ld", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %}; | ||||
|     bool defined() %code{% RETVAL = THIS->defined; %}; | ||||
| 
 | ||||
| %{ | ||||
| 
 | ||||
|  | @ -69,6 +70,7 @@ new_from_points(CLASS, points) | |||
|     void set_y_min(double val) %code{% THIS->min.y = val; %}; | ||||
|     void set_y_max(double val) %code{% THIS->max.y = val; %}; | ||||
|     std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf;%lf,%lf", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %}; | ||||
|     bool defined() %code{% RETVAL = THIS->defined; %}; | ||||
|      | ||||
| %{ | ||||
| 
 | ||||
|  | @ -106,4 +108,5 @@ new_from_points(CLASS, points) | |||
|     double z_min() %code{% RETVAL = THIS->min.z; %}; | ||||
|     double z_max() %code{% RETVAL = THIS->max.z; %}; | ||||
|     std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf;%lf,%lf,%lf", THIS->min.x, THIS->min.y, THIS->min.z, THIS->max.x, THIS->max.y, THIS->max.z); RETVAL = buf; %}; | ||||
|     bool defined() %code{% RETVAL = THIS->defined; %}; | ||||
| }; | ||||
|  |  | |||
|  | @ -16,58 +16,53 @@ _constant() | |||
|     JT_MITER        = jtMiter | ||||
|     JT_ROUND        = jtRound | ||||
|     JT_SQUARE       = jtSquare | ||||
|     CLIPPER_OFFSET_SCALE = CLIPPER_OFFSET_SCALE | ||||
|   CODE: | ||||
|     RETVAL = ix; | ||||
|   OUTPUT: RETVAL | ||||
| 
 | ||||
| Polygons | ||||
| offset(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||
| offset(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||
|     Polygons                polygons | ||||
|     const float             delta | ||||
|     double                  scale | ||||
|     ClipperLib::JoinType    joinType | ||||
|     double                  miterLimit | ||||
|     CODE: | ||||
|         offset(polygons, &RETVAL, delta, scale, joinType, miterLimit); | ||||
|         RETVAL = offset(polygons, delta, joinType, miterLimit); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
| ExPolygons | ||||
| offset_ex(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||
| offset_ex(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||
|     Polygons                polygons | ||||
|     const float             delta | ||||
|     double                  scale | ||||
|     ClipperLib::JoinType    joinType | ||||
|     double                  miterLimit | ||||
|     CODE: | ||||
|         offset(polygons, &RETVAL, delta, scale, joinType, miterLimit); | ||||
|         RETVAL = offset_ex(polygons, delta, joinType, miterLimit); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
| Polygons | ||||
| offset2(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||
| offset2(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||
|     Polygons                polygons | ||||
|     const float             delta1 | ||||
|     const float             delta2 | ||||
|     double                  scale | ||||
|     ClipperLib::JoinType    joinType | ||||
|     double                  miterLimit | ||||
|     CODE: | ||||
|         offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit); | ||||
|         RETVAL = offset2(polygons, delta1, delta2, joinType, miterLimit); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
| ExPolygons | ||||
| offset2_ex(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||
| offset2_ex(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||
|     Polygons                polygons | ||||
|     const float             delta1 | ||||
|     const float             delta2 | ||||
|     double                  scale | ||||
|     ClipperLib::JoinType    joinType | ||||
|     double                  miterLimit | ||||
|     CODE: | ||||
|         offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit); | ||||
|         RETVAL = offset2_ex(polygons, delta1, delta2, joinType, miterLimit); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  | @ -77,7 +72,7 @@ diff(subject, clip, safety_offset = false) | |||
|     Polygons    clip | ||||
|     bool        safety_offset | ||||
|     CODE: | ||||
|         diff(subject, clip, &RETVAL, safety_offset); | ||||
|         RETVAL = diff(subject, clip, safety_offset); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  | @ -87,7 +82,7 @@ diff_ex(subject, clip, safety_offset = false) | |||
|     Polygons    clip | ||||
|     bool        safety_offset | ||||
|     CODE: | ||||
|         diff(subject, clip, &RETVAL, safety_offset); | ||||
|         RETVAL = diff_ex(subject, clip, safety_offset); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  | @ -96,16 +91,7 @@ diff_pl(subject, clip) | |||
|     Polylines   subject | ||||
|     Polygons    clip | ||||
|     CODE: | ||||
|         diff(subject, clip, &RETVAL); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
| Polylines | ||||
| diff_ppl(subject, clip) | ||||
|     Polygons    subject | ||||
|     Polygons    clip | ||||
|     CODE: | ||||
|         diff(subject, clip, &RETVAL); | ||||
|         RETVAL = diff_pl(subject, clip); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  | @ -115,7 +101,7 @@ intersection(subject, clip, safety_offset = false) | |||
|     Polygons                    clip | ||||
|     bool                        safety_offset | ||||
|     CODE: | ||||
|         intersection(subject, clip, &RETVAL, safety_offset); | ||||
|         RETVAL = intersection(subject, clip, safety_offset); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  | @ -125,7 +111,7 @@ intersection_ex(subject, clip, safety_offset = false) | |||
|     Polygons                    clip | ||||
|     bool                        safety_offset | ||||
|     CODE: | ||||
|         intersection(subject, clip, &RETVAL, safety_offset); | ||||
|         RETVAL = intersection_ex(subject, clip, safety_offset); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  | @ -134,26 +120,7 @@ intersection_pl(subject, clip) | |||
|     Polylines                   subject | ||||
|     Polygons                    clip | ||||
|     CODE: | ||||
|         intersection(subject, clip, &RETVAL); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
| Polylines | ||||
| intersection_ppl(subject, clip) | ||||
|     Polygons                    subject | ||||
|     Polygons                    clip | ||||
|     CODE: | ||||
|         intersection(subject, clip, &RETVAL); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
| ExPolygons | ||||
| xor_ex(subject, clip, safety_offset = false) | ||||
|     Polygons                    subject | ||||
|     Polygons                    clip | ||||
|     bool                        safety_offset | ||||
|     CODE: | ||||
|         xor_(subject, clip, &RETVAL, safety_offset); | ||||
|         RETVAL = intersection_pl(subject, clip); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  | @ -162,7 +129,7 @@ union(subject, safety_offset = false) | |||
|     Polygons    subject | ||||
|     bool        safety_offset | ||||
|     CODE: | ||||
|         union_(subject, &RETVAL, safety_offset); | ||||
|         RETVAL = union_(subject, safety_offset); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  | @ -171,20 +138,7 @@ union_ex(subject, safety_offset = false) | |||
|     Polygons                    subject | ||||
|     bool                        safety_offset | ||||
|     CODE: | ||||
|         union_(subject, &RETVAL, safety_offset); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
| SV* | ||||
| union_pt(subject, safety_offset = false) | ||||
|     Polygons                    subject | ||||
|     bool                        safety_offset | ||||
|     CODE: | ||||
|         // perform operation | ||||
|         ClipperLib::PolyTree polytree; | ||||
|         union_pt(subject, &polytree, safety_offset); | ||||
|          | ||||
|         RETVAL = polynode_children_2_perl(polytree); | ||||
|         RETVAL = union_ex(subject, safety_offset); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  | @ -193,7 +147,7 @@ union_pt_chained(subject, safety_offset = false) | |||
|     Polygons                    subject | ||||
|     bool                        safety_offset | ||||
|     CODE: | ||||
|         union_pt_chained(subject, &RETVAL, safety_offset); | ||||
|         RETVAL = union_pt_chained(subject, safety_offset); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  | @ -201,7 +155,7 @@ Polygons | |||
| simplify_polygons(subject) | ||||
|     Polygons                    subject | ||||
|     CODE: | ||||
|         simplify_polygons(subject, &RETVAL); | ||||
|         RETVAL = simplify_polygons(subject); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  |  | |||
|  | @ -80,13 +80,12 @@ Polyline::rotate(angle, center_sv) | |||
|         THIS->rotate(angle, center); | ||||
| 
 | ||||
| Polygons | ||||
| Polyline::grow(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtSquare, miterLimit = 3) | ||||
| Polyline::grow(delta, joinType = ClipperLib::jtSquare, miterLimit = 3) | ||||
|     const float             delta | ||||
|     double                  scale | ||||
|     ClipperLib::JoinType    joinType | ||||
|     double                  miterLimit | ||||
|     CODE: | ||||
|         offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit); | ||||
|         RETVAL = offset(*THIS, delta, joinType, miterLimit); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| %{ | ||||
| #include <xsinit.h> | ||||
| #include "libslic3r/Print.hpp" | ||||
| #include "libslic3r/Slicing.hpp" | ||||
| #include "libslic3r/PlaceholderParser.hpp" | ||||
| %} | ||||
| 
 | ||||
|  | @ -58,6 +59,8 @@ _constant() | |||
|     Points copies(); | ||||
|     t_layer_height_ranges layer_height_ranges() | ||||
|         %code%{ RETVAL = THIS->layer_height_ranges; %}; | ||||
|     std::vector<double> layer_height_profile() | ||||
|         %code%{ RETVAL = THIS->layer_height_profile; %}; | ||||
|     Ref<Point3> size() | ||||
|         %code%{ RETVAL = &THIS->size; %}; | ||||
|     Clone<BoundingBox> bounding_box(); | ||||
|  | @ -82,6 +85,8 @@ _constant() | |||
|     bool reload_model_instances(); | ||||
|     void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges) | ||||
|         %code%{ THIS->layer_height_ranges = layer_height_ranges; %}; | ||||
|     void set_layer_height_profile(std::vector<double> profile) | ||||
|         %code%{ THIS->layer_height_profile = profile; %}; | ||||
| 
 | ||||
|     size_t total_layer_count(); | ||||
|     size_t layer_count(); | ||||
|  | @ -107,10 +112,31 @@ _constant() | |||
|     void set_step_started(PrintObjectStep step) | ||||
|         %code%{ THIS->state.set_started(step); %}; | ||||
| 
 | ||||
|     void _slice(); | ||||
|     void detect_surfaces_type(); | ||||
|     void process_external_surfaces(); | ||||
|     void discover_vertical_shells(); | ||||
|     void bridge_over_infill(); | ||||
|     void _make_perimeters(); | ||||
|     void _infill(); | ||||
|     void _generate_support_material(); | ||||
| 
 | ||||
|     void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) | ||||
|         %code%{  | ||||
|             THIS->update_layer_height_profile();  | ||||
|             adjust_layer_height_profile( | ||||
|                 THIS->slicing_parameters(), THIS->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action)); | ||||
|         %}; | ||||
|      | ||||
|     int generate_layer_height_texture(void *data, int rows, int cols, bool level_of_detail_2nd_level = true) | ||||
|         %code%{  | ||||
|             THIS->update_layer_height_profile(); | ||||
|             SlicingParameters slicing_params = THIS->slicing_parameters();  | ||||
|             RETVAL = generate_layer_height_texture( | ||||
|                 slicing_params,  | ||||
|                 generate_object_layers(slicing_params, THIS->layer_height_profile),  | ||||
|                 data, rows, cols, level_of_detail_2nd_level);  | ||||
|         %}; | ||||
|      | ||||
|     int ptr() | ||||
|         %code%{ RETVAL = (int)(intptr_t)THIS; %}; | ||||
|  |  | |||
|  | @ -1,26 +0,0 @@ | |||
| %module{Slic3r::XS}; | ||||
| 
 | ||||
| %{ | ||||
| #include <xsinit.h> | ||||
| #include "libslic3r/SupportMaterial.hpp" | ||||
| %} | ||||
| 
 | ||||
| %name{Slic3r::Print::SupportMaterial2} class PrintObjectSupportMaterial { | ||||
|     PrintObjectSupportMaterial(PrintObject *print_object); | ||||
|     ~PrintObjectSupportMaterial(); | ||||
| 
 | ||||
|     void generate(PrintObject *object) | ||||
|         %code{% THIS->generate(*object); %}; | ||||
| }; | ||||
| 
 | ||||
| %package{Slic3r::Print::SupportMaterial}; | ||||
| %{ | ||||
| 
 | ||||
| SV* | ||||
| MARGIN() | ||||
|     PROTOTYPE: | ||||
|     CODE: | ||||
|         RETVAL = newSVnv(SUPPORT_MATERIAL_MARGIN); | ||||
|     OUTPUT: RETVAL | ||||
| 
 | ||||
| %} | ||||
|  | @ -83,13 +83,12 @@ Surface::polygons() | |||
|         RETVAL | ||||
| 
 | ||||
| Surfaces | ||||
| Surface::offset(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||
| Surface::offset(delta, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||
|     const float             delta | ||||
|     double                  scale | ||||
|     ClipperLib::JoinType    joinType | ||||
|     double                  miterLimit | ||||
|     CODE: | ||||
|         offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit); | ||||
|         surfaces_append(RETVAL, offset_ex(THIS->expolygon, delta, joinType, miterLimit), *THIS); | ||||
|     OUTPUT: | ||||
|         RETVAL | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| %package{Slic3r::XS}; | ||||
| 
 | ||||
| #include <xsinit.h> | ||||
| #include "Utils.hpp" | ||||
| 
 | ||||
| %{ | ||||
| 
 | ||||
|  | @ -28,6 +29,12 @@ FORK_NAME() | |||
|         RETVAL = newSVpv(SLIC3R_FORK_NAME, 0); | ||||
|     OUTPUT: RETVAL | ||||
| 
 | ||||
| void | ||||
| set_logging_level(level) | ||||
|     unsigned int level; | ||||
|     CODE: | ||||
|         Slic3r::set_logging_level(level); | ||||
| 
 | ||||
| void | ||||
| xspp_test_croak_hangs_on_strawberry() | ||||
|     CODE: | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Eyal Soha
						Eyal Soha