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; |     printf @_ if $debug; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | our $loglevel = 0; | ||||||
|  | 
 | ||||||
| # load threads before Moo as required by it | # load threads before Moo as required by it | ||||||
| our $have_threads; | our $have_threads; | ||||||
| BEGIN { | BEGIN { | ||||||
|  | @ -104,6 +106,10 @@ my $pause_sema = Thread::Semaphore->new; | ||||||
| my $parallel_sema; | my $parallel_sema; | ||||||
| my $paused = 0; | 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 { | sub spawn_thread { | ||||||
|     my ($cb) = @_; |     my ($cb) = @_; | ||||||
|      |      | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ use Slic3r::GUI::ConfigWizard; | ||||||
| use Slic3r::GUI::Controller; | use Slic3r::GUI::Controller; | ||||||
| use Slic3r::GUI::Controller::ManualControlDialog; | use Slic3r::GUI::Controller::ManualControlDialog; | ||||||
| use Slic3r::GUI::Controller::PrinterPanel; | use Slic3r::GUI::Controller::PrinterPanel; | ||||||
|  | use Slic3r::GUI::GLShader; | ||||||
| use Slic3r::GUI::MainFrame; | use Slic3r::GUI::MainFrame; | ||||||
| use Slic3r::GUI::Notifier; | use Slic3r::GUI::Notifier; | ||||||
| use Slic3r::GUI::Plater; | use Slic3r::GUI::Plater; | ||||||
|  |  | ||||||
|  | @ -19,7 +19,8 @@ package Slic3r::GUI::3DScene::Base; | ||||||
| use strict; | use strict; | ||||||
| use warnings; | 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 | # must load OpenGL *before* Wx::GLCanvas | ||||||
| use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); | use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); | ||||||
| use base qw(Wx::GLCanvas Class::Accessor); | use base qw(Wx::GLCanvas Class::Accessor); | ||||||
|  | @ -53,10 +54,15 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init | ||||||
|                               origin |                               origin | ||||||
|                               _mouse_pos |                               _mouse_pos | ||||||
|                               _hover_volume_idx |                               _hover_volume_idx | ||||||
|  | 
 | ||||||
|                               _drag_volume_idx |                               _drag_volume_idx | ||||||
|                               _drag_start_pos |                               _drag_start_pos | ||||||
|                               _drag_start_xy |                               _drag_start_xy | ||||||
|                               _dragged |                               _dragged | ||||||
|  | 
 | ||||||
|  |                               layer_editing_enabled | ||||||
|  |                               _layer_height_edited | ||||||
|  | 
 | ||||||
|                               _camera_type |                               _camera_type | ||||||
|                               _camera_target |                               _camera_target | ||||||
|                               _camera_distance |                               _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_FRONT      => [0.0,90.0]; | ||||||
| use constant VIEW_REAR       => [180.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; | use constant GIMBALL_LOCK_THETA_MAX => 170; | ||||||
| 
 | 
 | ||||||
| # make OpenGL::Array thread-safe | # make OpenGL::Array thread-safe | ||||||
|  | @ -130,6 +139,15 @@ sub new { | ||||||
| #    $self->_camera_type('perspective'); | #    $self->_camera_type('perspective'); | ||||||
|     $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); |     $self->_camera_target(Slic3r::Pointf3->new(0,0,0)); | ||||||
|     $self->_camera_distance(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; |     $self->reset_objects; | ||||||
|      |      | ||||||
|  | @ -144,88 +162,142 @@ sub new { | ||||||
|         $self->Resize( $self->GetSizeWH ); |         $self->Resize( $self->GetSizeWH ); | ||||||
|         $self->Refresh; |         $self->Refresh; | ||||||
|     }); |     }); | ||||||
|     EVT_MOUSEWHEEL($self, sub { |     EVT_MOUSEWHEEL($self, \&mouse_wheel_event); | ||||||
|         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_MOUSE_EVENTS($self, \&mouse_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; |     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 { | sub mouse_event { | ||||||
|     my ($self, $e) = @_; |     my ($self, $e) = @_; | ||||||
|      |      | ||||||
|     my $pos = Slic3r::Pointf->new($e->GetPositionXY); |     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) { |     if ($e->Entering && &Wx::wxMSW) { | ||||||
|         # wxMSW needs focus in order to catch mouse wheel events |         # wxMSW needs focus in order to catch mouse wheel events | ||||||
|         $self->SetFocus; |         $self->SetFocus; | ||||||
|     } elsif ($e->LeftDClick) { |     } elsif ($e->LeftDClick) { | ||||||
|         $self->on_double_click->() |         if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { | ||||||
|             if $self->on_double_click; |         } elsif ($self->on_double_click) { | ||||||
|  |             $self->on_double_click->(); | ||||||
|  |         } | ||||||
|     } elsif ($e->LeftDown || $e->RightDown) { |     } elsif ($e->LeftDown || $e->RightDown) { | ||||||
|         # If user pressed left or right button we first check whether this happened |         # If user pressed left or right button we first check whether this happened | ||||||
|         # on a volume or not. |         # on a volume or not. | ||||||
|         my $volume_idx = $self->_hover_volume_idx // -1; |         my $volume_idx = $self->_hover_volume_idx // -1; | ||||||
|          |         $self->_layer_height_edited(0); | ||||||
|         # select volume in this 3D canvas |         if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) { | ||||||
|         if ($self->enable_picking) { |             # A volume is selected and the mouse is hovering over a layer thickness bar. | ||||||
|             $self->deselect_volumes; |             # Start editing the layer height. | ||||||
|             $self->select_volume($volume_idx); |             $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); | ||||||
|  |                  | ||||||
|  |                 if ($volume_idx != -1) { | ||||||
|  |                     my $group_id = $self->volumes->[$volume_idx]->select_group_id; | ||||||
|  |                     my @volumes; | ||||||
|  |                     if ($group_id != -1) { | ||||||
|  |                         $self->select_volume($_) | ||||||
|  |                             for grep $self->volumes->[$_]->select_group_id == $group_id, | ||||||
|  |                             0..$#{$self->volumes}; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|  |                 $self->Refresh; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             # propagate event through callback | ||||||
|  |             $self->on_select->($volume_idx) | ||||||
|  |                 if $self->on_select; | ||||||
|              |              | ||||||
|             if ($volume_idx != -1) { |             if ($volume_idx != -1) { | ||||||
|                 my $group_id = $self->volumes->[$volume_idx]->select_group_id; |                 if ($e->LeftDown && $self->enable_moving) { | ||||||
|                 my @volumes; |                     $self->_drag_volume_idx($volume_idx); | ||||||
|                 if ($group_id != -1) { |                     $self->_drag_start_pos($self->mouse_to_3d(@$pos)); | ||||||
|                     $self->select_volume($_) |                 } elsif ($e->RightDown) { | ||||||
|                         for grep $self->volumes->[$_]->select_group_id == $group_id, |                     # if right clicking on volume, propagate event through callback | ||||||
|                         0..$#{$self->volumes}; |                     $self->on_right_click->($e->GetPosition) | ||||||
|  |                         if $self->on_right_click; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|              |  | ||||||
|             $self->Refresh; |  | ||||||
|         } |         } | ||||||
|          |     } elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) { | ||||||
|         # propagate event through callback |  | ||||||
|         $self->on_select->($volume_idx) |  | ||||||
|             if $self->on_select; |  | ||||||
|          |  | ||||||
|         if ($volume_idx != -1) { |  | ||||||
|             if ($e->LeftDown && $self->enable_moving) { |  | ||||||
|                 $self->_drag_volume_idx($volume_idx); |  | ||||||
|                 $self->_drag_start_pos($self->mouse_to_3d(@$pos)); |  | ||||||
|             } elsif ($e->RightDown) { |  | ||||||
|                 # if right clicking on volume, propagate event through callback |  | ||||||
|                 $self->on_right_click->($e->GetPosition) |  | ||||||
|                     if $self->on_right_click; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } elsif ($e->Dragging && $e->LeftIsDown && defined($self->_drag_volume_idx)) { |  | ||||||
|         # get new position at the same Z of the initial click point |         # get new position at the same Z of the initial click point | ||||||
|         my $mouse_ray = $self->mouse_ray($e->GetX, $e->GetY); |         my $mouse_ray = $self->mouse_ray($e->GetX, $e->GetY); | ||||||
|         my $cur_pos = $mouse_ray->intersect_plane($self->_drag_start_pos->z); |         my $cur_pos = $mouse_ray->intersect_plane($self->_drag_start_pos->z); | ||||||
|  | @ -250,7 +322,9 @@ sub mouse_event { | ||||||
|         $self->_dragged(1); |         $self->_dragged(1); | ||||||
|         $self->Refresh; |         $self->Refresh; | ||||||
|     } elsif ($e->Dragging) { |     } 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 dragging over blank area with left button, rotate | ||||||
|             if (defined $self->_drag_start_pos) { |             if (defined $self->_drag_start_pos) { | ||||||
|                 my $orig = $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_pos(undef); | ||||||
|         $self->_drag_start_xy(undef); |         $self->_drag_start_xy(undef); | ||||||
|         $self->_dragged(undef); |         $self->_dragged(undef); | ||||||
|  |         $self->_layer_height_edited(undef); | ||||||
|  |         $self->{layer_height_edit_timer}->Stop; | ||||||
|     } elsif ($e->Moving) { |     } elsif ($e->Moving) { | ||||||
|         $self->_mouse_pos($pos); |         $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 { |     } else { | ||||||
|         $e->Skip(); |         $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. | # Reset selection. | ||||||
| sub reset_objects { | sub reset_objects { | ||||||
|     my ($self) = @_; |     my ($self) = @_; | ||||||
|  | @ -720,7 +841,22 @@ sub InitGL { | ||||||
|     return if $self->init; |     return if $self->init; | ||||||
|     return unless $self->GetContext; |     return unless $self->GetContext; | ||||||
|     $self->init(1); |     $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); |     glClearColor(0, 0, 0, 1); | ||||||
|     glColor3f(1, 0, 0); |     glColor3f(1, 0, 0); | ||||||
|     glEnable(GL_DEPTH_TEST); |     glEnable(GL_DEPTH_TEST); | ||||||
|  | @ -761,7 +897,14 @@ sub InitGL { | ||||||
|     glEnable(GL_COLOR_MATERIAL); |     glEnable(GL_COLOR_MATERIAL); | ||||||
|     glEnable(GL_MULTISAMPLE); |     glEnable(GL_MULTISAMPLE); | ||||||
| } | } | ||||||
|   | 
 | ||||||
|  | sub DestroyGL { | ||||||
|  |     my $self = shift; | ||||||
|  |     if ($self->init && $self->GetContext) { | ||||||
|  |         delete $self->{shader}; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| sub Render { | sub Render { | ||||||
|     my ($self, $dc) = @_; |     my ($self, $dc) = @_; | ||||||
|      |      | ||||||
|  | @ -798,11 +941,15 @@ sub Render { | ||||||
|     glLightfv_p(GL_LIGHT0, GL_POSITION, -0.5, -0.5, 1, 0); |     glLightfv_p(GL_LIGHT0, GL_POSITION, -0.5, -0.5, 1, 0); | ||||||
|     glLightfv_p(GL_LIGHT0, GL_SPECULAR, 0.2, 0.2, 0.2, 1); |     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); |     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) { |     if ($self->enable_picking) { | ||||||
|         # Render the object for 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. |         # 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. |         # Better to use software ray-casting on a bounding-box hierarchy. | ||||||
|  |         glDisable(GL_MULTISAMPLE); | ||||||
|         glDisable(GL_LIGHTING); |         glDisable(GL_LIGHTING); | ||||||
|         $self->draw_volumes(1); |         $self->draw_volumes(1); | ||||||
|         glFlush(); |         glFlush(); | ||||||
|  | @ -829,6 +976,7 @@ sub Render { | ||||||
|         glFlush(); |         glFlush(); | ||||||
|         glFinish(); |         glFinish(); | ||||||
|         glEnable(GL_LIGHTING); |         glEnable(GL_LIGHTING); | ||||||
|  |         glEnable(GL_MULTISAMPLE); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     # draw fixed background |     # draw fixed background | ||||||
|  | @ -928,7 +1076,7 @@ sub Render { | ||||||
|      |      | ||||||
|     # draw objects |     # draw objects | ||||||
|     $self->draw_volumes; |     $self->draw_volumes; | ||||||
|      | 
 | ||||||
|     # draw cutting plane |     # draw cutting plane | ||||||
|     if (defined $self->cutting_plane_z) { |     if (defined $self->cutting_plane_z) { | ||||||
|         my $plane_z = $self->cutting_plane_z; |         my $plane_z = $self->cutting_plane_z; | ||||||
|  | @ -947,10 +1095,13 @@ sub Render { | ||||||
|         glEnable(GL_CULL_FACE); |         glEnable(GL_CULL_FACE); | ||||||
|         glDisable(GL_BLEND); |         glDisable(GL_BLEND); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     $self->draw_active_object_annotations; | ||||||
|      |      | ||||||
|     glFlush(); |  | ||||||
|   |  | ||||||
|     $self->SwapBuffers(); |     $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 { | sub draw_volumes { | ||||||
|  | @ -963,10 +1114,61 @@ sub draw_volumes { | ||||||
|     glEnableClientState(GL_VERTEX_ARRAY); |     glEnableClientState(GL_VERTEX_ARRAY); | ||||||
|     glEnableClientState(GL_NORMAL_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}) { |     foreach my $volume_idx (0..$#{$self->volumes}) { | ||||||
|         my $volume = $self->volumes->[$volume_idx]; |         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. |             # Object picking mode. Render the object with a color encoding the object index. | ||||||
|             my $r = ($volume_idx & 0x000000FF) >>  0; |             my $r = ($volume_idx & 0x000000FF) >>  0; | ||||||
|             my $g = ($volume_idx & 0x0000FF00) >>  8; |             my $g = ($volume_idx & 0x0000FF00) >>  8; | ||||||
|  | @ -1017,16 +1219,39 @@ sub draw_volumes { | ||||||
|         if ($qverts_begin < $qverts_end) { |         if ($qverts_begin < $qverts_end) { | ||||||
|             glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr); |             glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr); | ||||||
|             glNormalPointer_c(GL_FLOAT, 0, $volume->qverts->norms_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) { |         if ($tverts_begin < $tverts_end) { | ||||||
|             glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr); |             glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr); | ||||||
|             glNormalPointer_c(GL_FLOAT, 0, $volume->tverts->norms_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(); |         glPopMatrix(); | ||||||
|  | 
 | ||||||
|  |         if ($shader_active) { | ||||||
|  |             glBindTexture(GL_TEXTURE_2D, 0); | ||||||
|  |             $self->{shader}->Disable; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     glDisableClientState(GL_NORMAL_ARRAY); |     glDisableClientState(GL_NORMAL_ARRAY); | ||||||
|     glDisable(GL_BLEND); |     glDisable(GL_BLEND); | ||||||
|  | @ -1036,10 +1261,98 @@ sub draw_volumes { | ||||||
|         glColor3f(0, 0, 0); |         glColor3f(0, 0, 0); | ||||||
|         glVertexPointer_p(3, $self->cut_lines_vertices); |         glVertexPointer_p(3, $self->cut_lines_vertices); | ||||||
|         glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3); |         glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3); | ||||||
|  |         glVertexPointer_c(3, GL_FLOAT, 0, 0); | ||||||
|     } |     } | ||||||
|     glDisableClientState(GL_VERTEX_ARRAY); |     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 | sub _report_opengl_state | ||||||
| { | { | ||||||
|     my ($self, $comment) = @_; |     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. | # Container for object geometry and selection status. | ||||||
| package Slic3r::GUI::3DScene::Volume; | package Slic3r::GUI::3DScene::Volume; | ||||||
| use Moo; | use Moo; | ||||||
|  | @ -1076,6 +1495,8 @@ use Moo; | ||||||
| has 'bounding_box'      => (is => 'ro', required => 1); | has 'bounding_box'      => (is => 'ro', required => 1); | ||||||
| has 'origin'            => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); | has 'origin'            => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) }); | ||||||
| has 'color'             => (is => 'ro', required => 1); | 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. | # 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 }); | 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. | # 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. | # The offsets stores tripples of (z_top, qverts_idx, tverts_idx) in a linear array. | ||||||
| has 'offsets'           => (is => 'rw'); | 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 { | sub transformed_bounding_box { | ||||||
|     my ($self) = @_; |     my ($self) = @_; | ||||||
|      |      | ||||||
|  | @ -1122,8 +1563,6 @@ __PACKAGE__->mk_accessors(qw( | ||||||
|     color_by |     color_by | ||||||
|     select_by |     select_by | ||||||
|     drag_by |     drag_by | ||||||
|     volumes_by_object |  | ||||||
|     _objects_by_volumes |  | ||||||
| )); | )); | ||||||
| 
 | 
 | ||||||
| sub new { | sub new { | ||||||
|  | @ -1133,14 +1572,12 @@ sub new { | ||||||
|     $self->color_by('volume');      # object | volume |     $self->color_by('volume');      # object | volume | ||||||
|     $self->select_by('object');     # object | volume | instance |     $self->select_by('object');     # object | volume | instance | ||||||
|     $self->drag_by('instance');     # object | 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; |     return $self; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub load_object { | sub load_object { | ||||||
|     my ($self, $model, $obj_idx, $instance_idxs) = @_; |     my ($self, $model, $print, $obj_idx, $instance_idxs) = @_; | ||||||
|      |      | ||||||
|     my $model_object; |     my $model_object; | ||||||
|     if ($model->isa('Slic3r::Model::Object')) { |     if ($model->isa('Slic3r::Model::Object')) { | ||||||
|  | @ -1152,6 +1589,19 @@ sub load_object { | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     $instance_idxs ||= [0..$#{$model_object->instances}]; |     $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 = (); |     my @volumes_idx = (); | ||||||
|     foreach my $volume_idx (0..$#{$model_object->volumes}) { |     foreach my $volume_idx (0..$#{$model_object->volumes}) { | ||||||
|  | @ -1174,16 +1624,18 @@ sub load_object { | ||||||
|             # not correspond to the color of the filament. |             # not correspond to the color of the filament. | ||||||
|             my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; |             my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; | ||||||
|             $color->[3] = $volume->modifier ? 0.5 : 1; |             $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( |             push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( | ||||||
|                 bounding_box    => $mesh->bounding_box, |                 bounding_box    => $mesh->bounding_box, | ||||||
|                 color           => $color, |                 color           => $color, | ||||||
|             ); |             ); | ||||||
|  |             $v->composite_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx); | ||||||
|             if ($self->select_by eq 'object') { |             if ($self->select_by eq 'object') { | ||||||
|                 $v->select_group_id($obj_idx*1000000); |                 $v->select_group_id($obj_idx*1000000); | ||||||
|             } elsif ($self->select_by eq 'volume') { |             } elsif ($self->select_by eq 'volume') { | ||||||
|                 $v->select_group_id($obj_idx*1000000 + $volume_idx*1000); |                 $v->select_group_id($obj_idx*1000000 + $volume_idx*1000); | ||||||
|             } elsif ($self->select_by eq 'instance') { |             } 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') { |             if ($self->drag_by eq 'object') { | ||||||
|                 $v->drag_group_id($obj_idx*1000); |                 $v->drag_group_id($obj_idx*1000); | ||||||
|  | @ -1191,15 +1643,18 @@ sub load_object { | ||||||
|                 $v->drag_group_id($obj_idx*1000 + $instance_idx); |                 $v->drag_group_id($obj_idx*1000 + $instance_idx); | ||||||
|             } |             } | ||||||
|             push @volumes_idx, my $scene_volume_idx = $#{$self->volumes}; |             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; |             my $verts = Slic3r::GUI::_3DScene::GLVertexArray->new; | ||||||
|             $verts->load_mesh($mesh); |             $verts->load_mesh($mesh); | ||||||
|             $v->tverts($verts); |             $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; |     return @volumes_idx; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1538,19 +1993,4 @@ sub _extrusionentity_to_verts { | ||||||
|         $tverts); |         $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; | 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_tabpanel; | ||||||
|     $self->_init_menubar; |     $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 |     # initialize status bar | ||||||
|     $self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1); |     $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"); |     $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 |     # View menu | ||||||
|     if (!$self->{no_plater}) { |     if (!$self->{no_plater}) { | ||||||
|         $self->{viewMenu} = Wx::Menu->new; |         $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}, "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}, "Bottom" , 'Bottom View' , sub { $self->select_view('bottom' ); }); | ||||||
|         $self->_append_menu_item($self->{viewMenu}, "Front"  , 'Front View'  , sub { $self->select_view('front'  ); }); |         $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 File::Basename qw(basename dirname); | ||||||
| use List::Util qw(sum first max); | use List::Util qw(sum first max); | ||||||
| use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad); | use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad); | ||||||
|  | use LWP::UserAgent; | ||||||
| use threads::shared qw(shared_clone); | use threads::shared qw(shared_clone); | ||||||
| use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc  | use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc  | ||||||
|     :panel :sizer :toolbar :window wxTheApp :notebook :combobox); |     :panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap); | ||||||
| use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED  | 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_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL  | ||||||
|     EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED); |     EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED); | ||||||
| use base 'Wx::Panel'; | use base 'Wx::Panel'; | ||||||
|  | @ -30,6 +31,7 @@ use constant TB_SCALE   => &Wx::NewId; | ||||||
| use constant TB_SPLIT   => &Wx::NewId; | use constant TB_SPLIT   => &Wx::NewId; | ||||||
| use constant TB_CUT     => &Wx::NewId; | use constant TB_CUT     => &Wx::NewId; | ||||||
| use constant TB_SETTINGS => &Wx::NewId; | use constant TB_SETTINGS => &Wx::NewId; | ||||||
|  | use constant TB_LAYER_EDITING => &Wx::NewId; | ||||||
| 
 | 
 | ||||||
| # package variables to avoid passing lexicals to threads | # package variables to avoid passing lexicals to threads | ||||||
| our $THUMBNAIL_DONE_EVENT    : shared = Wx::NewEventType; | our $THUMBNAIL_DONE_EVENT    : shared = Wx::NewEventType; | ||||||
|  | @ -94,7 +96,7 @@ sub new { | ||||||
|      |      | ||||||
|     # Initialize 3D plater |     # Initialize 3D plater | ||||||
|     if ($Slic3r::GUI::have_OpenGL) { |     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->{preview_notebook}->AddPage($self->{canvas3D}, '3D'); | ||||||
|         $self->{canvas3D}->set_on_select_object($on_select_object); |         $self->{canvas3D}->set_on_select_object($on_select_object); | ||||||
|         $self->{canvas3D}->set_on_double_click($on_double_click); |         $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}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), ''); | ||||||
|         $self->{htoolbar}->AddSeparator; |         $self->{htoolbar}->AddSeparator; | ||||||
|         $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); |         $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 { |     } else { | ||||||
|         my %tbar_buttons = ( |         my %tbar_buttons = ( | ||||||
|             add             => "Add…", |             add             => "Add…", | ||||||
|  | @ -168,12 +174,17 @@ sub new { | ||||||
|             split           => "Split", |             split           => "Split", | ||||||
|             cut             => "Cut…", |             cut             => "Cut…", | ||||||
|             settings        => "Settings…", |             settings        => "Settings…", | ||||||
|  |             layer_editing   => "Layer editing", | ||||||
|         ); |         ); | ||||||
|         $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL); |         $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL); | ||||||
|         for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) { |         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->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); | ||||||
|             $self->{btoolbar}->Add($self->{"btn_$_"}); |             $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, |     $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_SPLIT, sub { $self->split_object; }); | ||||||
|         EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog }); |         EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog }); | ||||||
|         EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_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 { |     } else { | ||||||
|         EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; }); |         EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; }); | ||||||
|         EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove |         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_split}, sub { $self->split_object; }); | ||||||
|         EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog }); |         EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog }); | ||||||
|         EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_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)) |     $_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self)) | ||||||
|  | @ -464,6 +481,12 @@ sub _on_select_preset { | ||||||
| 	$self->on_config_change($self->GetFrame->config); | 	$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 { | sub GetFrame { | ||||||
|     my ($self) = @_; |     my ($self) = @_; | ||||||
|     return &Wx::GetTopLevelParent($self); |     return &Wx::GetTopLevelParent($self); | ||||||
|  | @ -1481,6 +1504,8 @@ sub on_thumbnail_made { | ||||||
| # (i.e. when an object is added/removed/moved/rotated/scaled) | # (i.e. when an object is added/removed/moved/rotated/scaled) | ||||||
| sub update { | sub update { | ||||||
|     my ($self, $force_autocenter) = @_; |     my ($self, $force_autocenter) = @_; | ||||||
|  | 
 | ||||||
|  |     print "Platter - update\n"; | ||||||
|      |      | ||||||
|     if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) { |     if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) { | ||||||
|         $self->{model}->center_instances_around_point($self->bed_centerf); |         $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 $have_objects = @{$self->{objects}} ? 1 : 0; | ||||||
|     my $method = $have_objects ? 'Enable' : 'Disable'; |     my $method = $have_objects ? 'Enable' : 'Disable'; | ||||||
|     $self->{"btn_$_"}->$method |     $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}) { |     if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) { | ||||||
|         $self->{btn_reslice}->Disable; |         $self->{btn_reslice}->Disable; | ||||||
|  | @ -1712,6 +1737,7 @@ sub selection_changed { | ||||||
|     if ($self->{object_info_size}) { # have we already loaded the info pane? |     if ($self->{object_info_size}) { # have we already loaded the info pane? | ||||||
|         if ($have_sel) { |         if ($have_sel) { | ||||||
|             my $model_object = $self->{model}->objects->[$obj_idx]; |             my $model_object = $self->{model}->objects->[$obj_idx]; | ||||||
|  |             $model_object->print_info; | ||||||
|             my $model_instance = $model_object->instances->[0]; |             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_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size})); | ||||||
|             $self->{object_info_materials}->SetLabel($model_object->materials_count); |             $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 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) { |             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->SetPen($self->{clearance_pen}); | ||||||
|                 $dc->SetBrush($self->{transparent_brush}); |                 $dc->SetBrush($self->{transparent_brush}); | ||||||
|                 $dc->DrawPolygon($self->scaled_points_to_pixel($clearance, 1), 0, 0); |                 $dc->DrawPolygon($self->scaled_points_to_pixel($clearance, 1), 0, 0); | ||||||
|  | @ -185,7 +185,7 @@ sub repaint { | ||||||
|     if (@{$self->{objects}} && $self->{config}->skirts) { |     if (@{$self->{objects}} && $self->{config}->skirts) { | ||||||
|         my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$self->{objects}}; |         my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$self->{objects}}; | ||||||
|         if (@points >= 3) { |         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->SetPen($self->{skirt_pen}); | ||||||
|             $dc->SetBrush($self->{transparent_brush}); |             $dc->SetBrush($self->{transparent_brush}); | ||||||
|             $dc->DrawPolygon($self->scaled_points_to_pixel($convex_hull, 1), 0, 0); |             $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 { | sub new { | ||||||
|     my $class = shift; |     my $class = shift; | ||||||
|     my ($parent, $objects, $model, $config) = @_; |     my ($parent, $objects, $model, $print, $config) = @_; | ||||||
|      |      | ||||||
|     my $self = $class->SUPER::new($parent); |     my $self = $class->SUPER::new($parent); | ||||||
|     $self->enable_picking(1); |     $self->enable_picking(1); | ||||||
|  | @ -22,6 +22,7 @@ sub new { | ||||||
|      |      | ||||||
|     $self->{objects}            = $objects; |     $self->{objects}            = $objects; | ||||||
|     $self->{model}              = $model; |     $self->{model}              = $model; | ||||||
|  |     $self->{print}              = $print; | ||||||
|     $self->{config}             = $config; |     $self->{config}             = $config; | ||||||
|     $self->{on_select_object}   = sub {}; |     $self->{on_select_object}   = sub {}; | ||||||
|     $self->{on_instances_moved} = sub {}; |     $self->{on_instances_moved} = sub {}; | ||||||
|  | @ -31,7 +32,7 @@ sub new { | ||||||
|          |          | ||||||
|         my $obj_idx = undef; |         my $obj_idx = undef; | ||||||
|         if ($volume_idx != -1) { |         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) |         $self->{on_select_object}->($obj_idx) | ||||||
|             if $self->{on_select_object}; |             if $self->{on_select_object}; | ||||||
|  | @ -42,8 +43,8 @@ sub new { | ||||||
|         my %done = ();  # prevent moving instances twice |         my %done = ();  # prevent moving instances twice | ||||||
|         foreach my $volume_idx (@volume_idxs) { |         foreach my $volume_idx (@volume_idxs) { | ||||||
|             my $volume = $self->volumes->[$volume_idx]; |             my $volume = $self->volumes->[$volume_idx]; | ||||||
|             my $obj_idx = $self->object_idx($volume_idx); |             my $obj_idx = $volume->object_idx; | ||||||
|             my $instance_idx = $self->instance_idx($volume_idx); |             my $instance_idx = $volume->instance_idx; | ||||||
|             next if $done{"${obj_idx}_${instance_idx}"}; |             next if $done{"${obj_idx}_${instance_idx}"}; | ||||||
|             $done{"${obj_idx}_${instance_idx}"} = 1; |             $done{"${obj_idx}_${instance_idx}"} = 1; | ||||||
|              |              | ||||||
|  | @ -89,7 +90,7 @@ sub update { | ||||||
|     $self->update_bed_size; |     $self->update_bed_size; | ||||||
|      |      | ||||||
|     foreach my $obj_idx (0..$#{$self->{model}->objects}) { |     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) { |         if ($self->{objects}[$obj_idx]->selected) { | ||||||
|             $self->select_volume($_) for @volume_idxs; |             $self->select_volume($_) for @volume_idxs; | ||||||
|  |  | ||||||
|  | @ -114,7 +114,7 @@ sub new { | ||||||
|     my $canvas; |     my $canvas; | ||||||
|     if ($Slic3r::GUI::have_OpenGL) { |     if ($Slic3r::GUI::have_OpenGL) { | ||||||
|         $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self); |         $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->set_auto_bed_shape; | ||||||
|         $canvas->SetSize([500,500]); |         $canvas->SetSize([500,500]); | ||||||
|         $canvas->SetMinSize($canvas->GetSize); |         $canvas->SetMinSize($canvas->GetSize); | ||||||
|  | @ -244,7 +244,7 @@ sub _update { | ||||||
|             } |             } | ||||||
|              |              | ||||||
|             $self->{canvas}->reset_objects; |             $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->{canvas}->SetCuttingPlane( | ||||||
|                 $self->{cut_options}{z}, |                 $self->{cut_options}{z}, | ||||||
|                 [@expolygons], |                 [@expolygons], | ||||||
|  |  | ||||||
|  | @ -22,6 +22,7 @@ sub new { | ||||||
|     my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); |     my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); | ||||||
|      |      | ||||||
|     my $object = $self->{model_object} = $params{model_object}; |     my $object = $self->{model_object} = $params{model_object}; | ||||||
|  |     my $print_object = $self->{print_object} = $params{print_object}; | ||||||
|      |      | ||||||
|     # create TreeCtrl |     # create TreeCtrl | ||||||
|     my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100],  |     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)); |             $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->set_auto_bed_shape; | ||||||
|         $canvas->SetSize([500,500]); |         $canvas->SetSize([500,500]); | ||||||
|         $canvas->zoom_to_volumes; |         $canvas->zoom_to_volumes; | ||||||
|  |  | ||||||
|  | @ -523,6 +523,7 @@ sub build { | ||||||
|         max_volumetric_extrusion_rate_slope_positive max_volumetric_extrusion_rate_slope_negative |         max_volumetric_extrusion_rate_slope_positive max_volumetric_extrusion_rate_slope_negative | ||||||
|         perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed  |         perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed  | ||||||
|         solid_infill_speed top_solid_infill_speed support_material_speed  |         solid_infill_speed top_solid_infill_speed support_material_speed  | ||||||
|  |         support_material_xy_spacing | ||||||
|         support_material_interface_speed bridge_speed gap_fill_speed |         support_material_interface_speed bridge_speed gap_fill_speed | ||||||
|         travel_speed |         travel_speed | ||||||
|         first_layer_speed |         first_layer_speed | ||||||
|  | @ -532,8 +533,8 @@ sub build { | ||||||
|         brim_width |         brim_width | ||||||
|         support_material support_material_threshold support_material_enforce_layers |         support_material support_material_threshold support_material_enforce_layers | ||||||
|         raft_layers |         raft_layers | ||||||
|         support_material_pattern support_material_with_sheath support_material_spacing support_material_angle |         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_layers support_material_interface_spacing support_material_interface_contact_loops | ||||||
|         support_material_contact_distance support_material_buildplate_only dont_support_bridges |         support_material_contact_distance support_material_buildplate_only dont_support_bridges | ||||||
|         notes |         notes | ||||||
|         complete_objects extruder_clearance_radius extruder_clearance_height |         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_angle'); | ||||||
|             $optgroup->append_single_option_line('support_material_interface_layers'); |             $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_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_buildplate_only'); | ||||||
|  |             $optgroup->append_single_option_line('support_material_xy_spacing'); | ||||||
|             $optgroup->append_single_option_line('dont_support_bridges'); |             $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; |     my $have_support_interface = $config->support_material_interface_layers > 0; | ||||||
|     $self->get_field($_)->toggle($have_support_material) |     $self->get_field($_)->toggle($have_support_material) | ||||||
|         for qw(support_material_threshold support_material_pattern support_material_with_sheath |         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_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) |     $self->get_field($_)->toggle($have_support_material && $have_support_interface) | ||||||
|         for qw(support_material_interface_spacing support_material_interface_extruder |         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('perimeter_extrusion_width')->toggle($have_perimeters || $have_skirt || $have_brim); | ||||||
|     $self->get_field('support_material_extruder')->toggle($have_support_material || $have_skirt); |     $self->get_field('support_material_extruder')->toggle($have_support_material || $have_skirt); | ||||||
|  |  | ||||||
|  | @ -5,9 +5,9 @@ use warnings; | ||||||
| require Exporter; | require Exporter; | ||||||
| our @ISA = qw(Exporter); | our @ISA = qw(Exporter); | ||||||
| our @EXPORT_OK = qw(offset offset_ex | our @EXPORT_OK = qw(offset offset_ex | ||||||
|     diff_ex diff union_ex intersection_ex xor_ex JT_ROUND JT_MITER |     diff_ex diff union_ex intersection_ex JT_ROUND JT_MITER | ||||||
|     JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex |     JT_SQUARE is_counter_clockwise offset2 offset2_ex | ||||||
|     intersection intersection_pl diff_pl union CLIPPER_OFFSET_SCALE |     intersection intersection_pl diff_pl union | ||||||
|     union_pt_chained diff_ppl intersection_ppl); |     union_pt_chained); | ||||||
| 
 | 
 | ||||||
| 1; | 1; | ||||||
|  |  | ||||||
|  | @ -41,8 +41,12 @@ sub size { | ||||||
| sub process { | sub process { | ||||||
|     my ($self) = @_; |     my ($self) = @_; | ||||||
|      |      | ||||||
|  |     $self->status_cb->(20, "Generating perimeters"); | ||||||
|     $_->make_perimeters for @{$self->objects}; |     $_->make_perimeters for @{$self->objects}; | ||||||
|  |      | ||||||
|  |     $self->status_cb->(70, "Infilling layers"); | ||||||
|     $_->infill for @{$self->objects}; |     $_->infill for @{$self->objects}; | ||||||
|  |      | ||||||
|     $_->generate_support_material for @{$self->objects}; |     $_->generate_support_material for @{$self->objects}; | ||||||
|     $self->make_skirt; |     $self->make_skirt; | ||||||
|     $self->make_brim;  # must come after 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); |     my $distance = scale max($self->config->skirt_distance, $self->config->brim_width); | ||||||
|     for (my $i = $skirts; $i > 0; $i--) { |     for (my $i = $skirts; $i > 0; $i--) { | ||||||
|         $distance += scale $spacing; |         $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( |         my $eloop = Slic3r::ExtrusionLoop->new_from_paths( | ||||||
|             Slic3r::ExtrusionPath->new( |             Slic3r::ExtrusionPath->new( | ||||||
|                 polyline        => Slic3r::Polygon->new(@$loop)->split_at_first_point, |                 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 |         # -0.5 because islands are not represented by their centerlines | ||||||
|         # (first offset more, then step back - reverse order than the one used for  |         # (first offset more, then step back - reverse order than the one used for  | ||||||
|         # perimeters because here we're offsetting outwards) |         # 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( |     $self->brim->append(map Slic3r::ExtrusionLoop->new_from_paths( | ||||||
|  |  | ||||||
|  | @ -262,7 +262,10 @@ sub export { | ||||||
|                     $gcodegen->avoid_crossing_perimeters->set_disable_once(1); |                     $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) { |                 for my $layer (@layers) { | ||||||
|                     # if we are printing the bottom layer of an object, and we have already finished |                     # 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 |                     # 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 |         my %layers = ();  # print_z => [ [layers], [layers], [layers] ]  by obj_idx | ||||||
|         foreach my $obj_idx (0 .. ($self->print->object_count - 1)) { |         foreach my $obj_idx (0 .. ($self->print->object_count - 1)) { | ||||||
|             my $object = $self->objects->[$obj_idx]; |             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 } ||= []; | ||||||
|                 $layers{ $layer->print_z }[$obj_idx] ||= []; |                 $layers{ $layer->print_z }[$obj_idx] ||= []; | ||||||
|                 push @{$layers{ $layer->print_z }[$obj_idx]}, $layer; |                 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::Flow ':roles'; | ||||||
| use Slic3r::Geometry qw(X Y Z PI scale unscale chained_path epsilon); | 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  | 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::Print::State ':steps'; | ||||||
| use Slic3r::Surface ':types'; | use Slic3r::Surface ':types'; | ||||||
| 
 | 
 | ||||||
|  | @ -45,223 +45,7 @@ sub slice { | ||||||
|     $self->set_step_started(STEP_SLICE); |     $self->set_step_started(STEP_SLICE); | ||||||
|     $self->print->status_cb->(10, "Processing triangulated mesh"); |     $self->print->status_cb->(10, "Processing triangulated mesh"); | ||||||
|      |      | ||||||
|     # init layers |     $self->_slice; | ||||||
|     { |  | ||||||
|         $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; |  | ||||||
|     } |  | ||||||
|      |      | ||||||
|     # detect slicing errors |     # detect slicing errors | ||||||
|     my $warning_thrown = 0; |     my $warning_thrown = 0; | ||||||
|  | @ -334,151 +118,15 @@ sub slice { | ||||||
|     $self->set_step_done(STEP_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. | # 1) Merges typed region slices into stInternal type. | ||||||
| # 2) Increases an "extra perimeters" counter at region slices where needed. | # 2) Increases an "extra perimeters" counter at region slices where needed. | ||||||
| # 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal). | # 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal). | ||||||
| sub make_perimeters { | sub make_perimeters { | ||||||
|     my $self = shift; |     my ($self) = @_; | ||||||
|      |      | ||||||
|     # prerequisites |     # prerequisites | ||||||
|     $self->slice; |     $self->slice; | ||||||
|      |     $self->_make_perimeters; | ||||||
|     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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub prepare_infill { | sub prepare_infill { | ||||||
|  | @ -598,32 +246,7 @@ sub infill { | ||||||
|      |      | ||||||
|     # prerequisites |     # prerequisites | ||||||
|     $self->prepare_infill; |     $self->prepare_infill; | ||||||
|      |     $self->_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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub generate_support_material { | sub generate_support_material { | ||||||
|  | @ -637,44 +260,35 @@ sub generate_support_material { | ||||||
|      |      | ||||||
|     $self->clear_support_layers; |     $self->clear_support_layers; | ||||||
|      |      | ||||||
|     if ((!$self->config->support_material && $self->config->raft_layers == 0) || scalar(@{$self->layers}) < 2) { |     if (($self->config->support_material || $self->config->raft_layers > 0) && scalar(@{$self->layers}) > 1) { | ||||||
|         $self->set_step_done(STEP_SUPPORTMATERIAL); |         $self->print->status_cb->(85, "Generating support material");     | ||||||
|         return; |         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, | ||||||
|  |                 nozzle_diameter     => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ] | ||||||
|  |                                         // $self->print->config->nozzle_diameter->[0], | ||||||
|  |                 layer_height        => $self->config->get_abs_value('first_layer_height'), | ||||||
|  |                 bridge_flow_ratio   => 0, | ||||||
|  |             );             | ||||||
|  |             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. | ||||||
|  |             $self->_generate_support_material; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     $self->print->status_cb->(85, "Generating support material"); |  | ||||||
|      |  | ||||||
|     $self->_support_material->generate($self); |  | ||||||
|      |      | ||||||
|     $self->set_step_done(STEP_SUPPORTMATERIAL); |     $self->set_step_done(STEP_SUPPORTMATERIAL); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub _support_material { |  | ||||||
|     my ($self) = @_; |  | ||||||
|      |  | ||||||
|     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, |  | ||||||
|         nozzle_diameter     => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ] |  | ||||||
|                                 // $self->print->config->nozzle_diameter->[0], |  | ||||||
|         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( |  | ||||||
|             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), |  | ||||||
|         ); |  | ||||||
|     } else { |  | ||||||
|         # New supports, C++ implementation. |  | ||||||
|         return Slic3r::Print::SupportMaterial2->new($self); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| # Idempotence of this method is guaranteed by the fact that we don't remove things from | # Idempotence of this method is guaranteed by the fact that we don't remove things from | ||||||
| # fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. | # fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. | ||||||
| sub clip_fill_surfaces { | sub clip_fill_surfaces { | ||||||
|  | @ -863,7 +477,7 @@ sub discover_horizontal_shells { | ||||||
|                         # and it's not wanted in a hollow print even if it would make sense when |                         # and it's not wanted in a hollow print even if it would make sense when | ||||||
|                         # obeying the solid shell count option strictly (DWIM!) |                         # obeying the solid shell count option strictly (DWIM!) | ||||||
|                         my $margin = $neighbor_layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width; |                         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( |                         my $too_narrow = diff( | ||||||
|                             $new_internal_solid, |                             $new_internal_solid, | ||||||
|                             $regularized, |                             $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. |                         # have the same angle, so the next shell would be grown even more and so on. | ||||||
|                         my $too_narrow = diff( |                         my $too_narrow = diff( | ||||||
|                             $new_internal_solid, |                             $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, |                             1, | ||||||
|                         ); |                         ); | ||||||
|                          |                          | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ use Slic3r::ExtrusionPath ':roles'; | ||||||
| use Slic3r::Flow ':roles'; | use Slic3r::Flow ':roles'; | ||||||
| use Slic3r::Geometry qw(epsilon scale scaled_epsilon PI rad2deg deg2rad convex_hull); | 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 | 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'; | use Slic3r::Surface ':types'; | ||||||
| 
 | 
 | ||||||
| has 'print_config'      => (is => 'rw', required => 1); | has 'print_config'      => (is => 'rw', required => 1); | ||||||
|  | @ -20,6 +20,7 @@ has 'interface_flow'    => (is => 'rw', required => 1); | ||||||
| use constant DEBUG_CONTACT_ONLY => 0; | use constant DEBUG_CONTACT_ONLY => 0; | ||||||
| 
 | 
 | ||||||
| # increment used to reach MARGIN in steps to avoid trespassing thin objects | # increment used to reach MARGIN in steps to avoid trespassing thin objects | ||||||
|  | use constant MARGIN => 1.5; | ||||||
| use constant MARGIN_STEP => MARGIN/3; | use constant MARGIN_STEP => MARGIN/3; | ||||||
| 
 | 
 | ||||||
| # generate a tree-like structure to save material | # 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 |     # We now know the upper and lower boundaries for our support material object | ||||||
|     # (@$contact_z and @$top_z), so we can generate intermediate layers. |     # (@$contact_z and @$top_z), so we can generate intermediate layers. | ||||||
|     my $support_z = $self->support_layers_z( |     my $support_z = $self->support_layers_z( | ||||||
|  |         $object, | ||||||
|         [ sort keys %$contact ], |         [ sort keys %$contact ], | ||||||
|         [ sort keys %$top ], |         [ sort keys %$top ], | ||||||
|         max(map $_->height, @{$object->layers}) |         max(map $_->height, @{$object->layers}) | ||||||
|  | @ -299,9 +301,8 @@ sub contact_area { | ||||||
|                             offset( |                             offset( | ||||||
|                                 $diff,  |                                 $diff,  | ||||||
|                                 $_, |                                 $_, | ||||||
|                                 CLIPPER_OFFSET_SCALE, |  | ||||||
|                                 JT_ROUND, |                                 JT_ROUND, | ||||||
|                                 scale(0.05)*CLIPPER_OFFSET_SCALE), |                                 scale(0.05)), | ||||||
|                             $slices_margin |                             $slices_margin | ||||||
|                         ); |                         ); | ||||||
|                     } |                     } | ||||||
|  | @ -371,7 +372,7 @@ sub object_top { | ||||||
|                 # grow top surfaces so that interface and support generation are generated |                 # grow top surfaces so that interface and support generation are generated | ||||||
|                 # with some spacing from object - it looks we don't need the actual |                 # with some spacing from object - it looks we don't need the actual | ||||||
|                 # top shapes so this can be done here |                 # 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  |             # remove the areas that touched from the projection that will continue on  | ||||||
|  | @ -384,7 +385,7 @@ sub object_top { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub support_layers_z { | 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 |     # quick table to check whether a given Z is a top surface | ||||||
|     my %top = map { $_ => 1 } @$top_z; |     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); |     my $contact_distance = $self->contact_distance($support_material_height, $nozzle_diameter); | ||||||
|      |      | ||||||
|     # initialize known, fixed, support layers |     # initialize known, fixed, support layers | ||||||
|     my @z = sort { $a <=> $b } |     my @z = @$contact_z; | ||||||
|         @$contact_z, |     my $synchronize = $self->object_config->support_material_synchronize_layers; | ||||||
|         # TODO: why we have this? |     if (! $synchronize) { | ||||||
|         # Vojtech: To detect the bottom interface layers by finding a Z value in the $top_z. |         push @z,  | ||||||
|         @$top_z, |             # TODO: why we have this? | ||||||
|         # Top surfaces of the bottom interface layers. |             # Vojtech: To detect the bottom interface layers by finding a Z value in the $top_z. | ||||||
|         (map $_ + $contact_distance, @$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 |     # enforce first layer height | ||||||
|     my $first_layer_height = $self->object_config->get_value('first_layer_height'); |     my $first_layer_height = $self->object_config->get_value('first_layer_height'); | ||||||
|  | @ -423,23 +429,29 @@ sub support_layers_z { | ||||||
|             1..($self->object_config->raft_layers - 2); |             1..($self->object_config->raft_layers - 2); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     # create other layers (skip raft layers as they're already done and use thicker layers) |     if ($synchronize) { | ||||||
|     for (my $i = $#z; $i >= $self->object_config->raft_layers; $i--) { |         @z = splice @z, $self->object_config->raft_layers; | ||||||
|         my $target_height = $support_material_height; | #            if ($self->object_config->raft_layers > scalar(@z)); | ||||||
|         if ($i > 0 && $top{ $z[$i-1] }) { |         push @z, map $_->print_z, @{$object->layers}; | ||||||
|             # Bridge flow? |     } else { | ||||||
|             #FIXME We want to enforce not only the bridge flow height, but also the interface gap! |         # create other layers (skip raft layers as they're already done and use thicker layers) | ||||||
|             # This will introduce an additional layer if the gap is set to an extreme value! |         for (my $i = $#z; $i >= $self->object_config->raft_layers; $i--) { | ||||||
|             $target_height = $nozzle_diameter; |             my $target_height = $support_material_height; | ||||||
|         } |             if ($i > 0 && $top{ $z[$i-1] }) { | ||||||
|          |                 # Bridge flow? | ||||||
|         # enforce first layer height |                 #FIXME We want to enforce not only the bridge flow height, but also the interface gap! | ||||||
|         #FIXME better to split the layers regularly, than to bite a constant height one at a time,  |                 # This will introduce an additional layer if the gap is set to an extreme value! | ||||||
|         # and then be left with a very thin layer at the end. |                 $target_height = $nozzle_diameter; | ||||||
|         if (($i == 0 && $z[$i] > $target_height + $first_layer_height) |             } | ||||||
|             || ($z[$i] - $z[$i-1] > $target_height + Slic3r::Geometry::epsilon)) { |              | ||||||
|             splice @z, $i, 0, ($z[$i] - $target_height); |             # enforce first layer height | ||||||
|             $i++; |             #FIXME better to split the layers regularly, than to bite a constant height one at a time,  | ||||||
|  |             # and then be left with a very thin layer at the end. | ||||||
|  |             if (($i == 0 && $z[$i] > $target_height + $first_layer_height) | ||||||
|  |                 || ($z[$i] - $z[$i-1] > $target_height + Slic3r::Geometry::epsilon)) { | ||||||
|  |                 splice @z, $i, 0, ($z[$i] - $target_height); | ||||||
|  |                 $i++; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  | @ -584,9 +596,8 @@ sub generate_base_layers { | ||||||
|                         $fillet_radius_scaled,  |                         $fillet_radius_scaled,  | ||||||
|                         -$fillet_radius_scaled, |                         -$fillet_radius_scaled, | ||||||
|                         # Use a geometric offsetting for filleting. |                         # Use a geometric offsetting for filleting. | ||||||
|                         CLIPPER_OFFSET_SCALE, |  | ||||||
|                         JT_ROUND, |                         JT_ROUND, | ||||||
|                         0.2*$fillet_radius_scaled*CLIPPER_OFFSET_SCALE), |                         0.2*$fillet_radius_scaled), | ||||||
|                     $trim_polygons, |                     $trim_polygons, | ||||||
|                     0); # don't apply the safety offset. |                     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 |         # $layer->slices contains the full shape of layer, thus including | ||||||
|         # perimeter's width. $support contains the full shape of support |         # perimeter's width. $support contains the full shape of support | ||||||
|         # material, thus including the width of its foremost extrusion. |         # 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} = diff( | ||||||
|             $support->{$i}, |             $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; |     my $interface_flow  = $self->interface_flow; | ||||||
|      |      | ||||||
|     # shape of contact area |     # shape of contact area | ||||||
|     my $contact_loops   = 1; |     my $contact_loops   = $self->object_config->support_material_interface_contact_loops ? 1 : 0; | ||||||
|     my $circle_radius   = 1.5 * $interface_flow->scaled_width; |     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_distance = 3 * $circle_radius; | ||||||
|     my $circle          = Slic3r::Polygon->new(map [ $circle_radius * cos $_, $circle_radius * sin $_ ], |     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)); |                             (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 |             # if no interface layers were requested we treat the contact layer | ||||||
|             # exactly as a generic base layer |             # exactly as a generic base layer | ||||||
|             push @$base, @$contact; |             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 |             # generate the outermost loop | ||||||
|              |              | ||||||
|             # find centerline of the external loop (or any other kind of extrusions should the loop be skipped) |             # 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 = (); |             my @loops0 = (); | ||||||
|             { |             { | ||||||
|  |  | ||||||
|  | @ -426,6 +426,9 @@ $j | ||||||
|                         Support material angle in degrees (range: 0-90, default: $config->{support_material_angle}) |                         Support material angle in degrees (range: 0-90, default: $config->{support_material_angle}) | ||||||
|     --support-material-contact-distance |     --support-material-contact-distance | ||||||
|                         Vertical distance between object and support material (0+, default: $config->{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 |     --support-material-interface-layers | ||||||
|                         Number of perpendicular layers between support material and object (0+, default: $config->{support_material_interface_layers}) |                         Number of perpendicular layers between support material and object (0+, default: $config->{support_material_interface_layers}) | ||||||
|     --support-material-interface-spacing |     --support-material-interface-spacing | ||||||
|  |  | ||||||
|  | @ -28,7 +28,7 @@ use Slic3r::Test; | ||||||
|             interface_flow      => $flow, |             interface_flow      => $flow, | ||||||
|             first_layer_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]); |         my $expected_top_spacing = $support->contact_distance($config->layer_height, $config->nozzle_diameter->[0]); | ||||||
|          |          | ||||||
|         is $support_z->[0], $config->first_layer_height, |         is $support_z->[0], $config->first_layer_height, | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								xs/Build.PL
									
										
									
									
									
								
							
							
						
						
									
										27
									
								
								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 | # In order to generate the -l switches we need to know how Boost libraries are named | ||||||
| my $have_boost = 0; | 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 (!$ENV{SLIC3R_STATIC}) { | ||||||
| if (! $mswin) { |     # Dynamic linking of boost libraries. | ||||||
|     $have_boost = 1 |     push @cflags, qw(-DBOOST_LOG_DYN_LINK); | ||||||
|         if check_lib( |     if (! $mswin) { | ||||||
|             lib     => [ map "boost_${_}", @boost_libraries ], |         # 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. |     # The boost library was detected by check_lib on Linux. | ||||||
|     push @LIBS, map "-lboost_${_}", @boost_libraries; |     push @LIBS, map "-lboost_${_}", @boost_libraries; | ||||||
| } else { | } else { | ||||||
|  | @ -138,8 +142,7 @@ if (!$ENV{SLIC3R_STATIC} && $have_boost) { | ||||||
|         # Try to find the boost system library. |         # Try to find the boost system library. | ||||||
|         my @files = glob "$path/${lib_prefix}system*$lib_ext"; |         my @files = glob "$path/${lib_prefix}system*$lib_ext"; | ||||||
|         next if !@files; |         next if !@files; | ||||||
|      |         if ($files[0] =~ /\Q${lib_prefix}system\E([^.]*)\Q$lib_ext\E$/) { | ||||||
|         if ($files[0] =~ /${lib_prefix}system([^.]+)$lib_ext$/) { |  | ||||||
|             # Suffix contains the version number, the build type etc. |             # Suffix contains the version number, the build type etc. | ||||||
|             my $suffix = $1; |             my $suffix = $1; | ||||||
|             # Verify existence of all required boost libraries at $path. |             # 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( | my $build = Module::Build::WithXSpp->new( | ||||||
|     module_name     => 'Slic3r::XS', |     module_name     => 'Slic3r::XS', | ||||||
|     dist_abstract   => 'XS code for Slic3r', |     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/ExtrusionEntityCollection.hpp | ||||||
| src/libslic3r/ExtrusionSimulator.cpp | src/libslic3r/ExtrusionSimulator.cpp | ||||||
| src/libslic3r/ExtrusionSimulator.hpp | src/libslic3r/ExtrusionSimulator.hpp | ||||||
|  | src/libslic3r/Fill/Fill.cpp | ||||||
|  | src/libslic3r/Fill/Fill.hpp | ||||||
| src/libslic3r/Fill/FillBase.cpp | src/libslic3r/Fill/FillBase.cpp | ||||||
| src/libslic3r/Fill/FillBase.hpp | src/libslic3r/Fill/FillBase.hpp | ||||||
| src/libslic3r/Fill/FillConcentric.cpp | src/libslic3r/Fill/FillConcentric.cpp | ||||||
|  | @ -54,6 +56,8 @@ src/libslic3r/GCodeSender.cpp | ||||||
| src/libslic3r/GCodeSender.hpp | src/libslic3r/GCodeSender.hpp | ||||||
| src/libslic3r/GCodeWriter.cpp | src/libslic3r/GCodeWriter.cpp | ||||||
| src/libslic3r/GCodeWriter.hpp | src/libslic3r/GCodeWriter.hpp | ||||||
|  | src/libslic3r/GCode/Analyzer.cpp | ||||||
|  | src/libslic3r/GCode/Analyzer.hpp | ||||||
| src/libslic3r/GCode/PressureEqualizer.cpp | src/libslic3r/GCode/PressureEqualizer.cpp | ||||||
| src/libslic3r/GCode/PressureEqualizer.hpp | src/libslic3r/GCode/PressureEqualizer.hpp | ||||||
| src/libslic3r/Geometry.cpp | src/libslic3r/Geometry.cpp | ||||||
|  | @ -88,6 +92,10 @@ src/libslic3r/PrintConfig.cpp | ||||||
| src/libslic3r/PrintConfig.hpp | src/libslic3r/PrintConfig.hpp | ||||||
| src/libslic3r/PrintObject.cpp | src/libslic3r/PrintObject.cpp | ||||||
| src/libslic3r/PrintRegion.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.cpp | ||||||
| src/libslic3r/SupportMaterial.hpp | src/libslic3r/SupportMaterial.hpp | ||||||
| src/libslic3r/Surface.cpp | src/libslic3r/Surface.cpp | ||||||
|  | @ -99,6 +107,7 @@ src/libslic3r/SVG.hpp | ||||||
| src/libslic3r/TriangleMesh.cpp | src/libslic3r/TriangleMesh.cpp | ||||||
| src/libslic3r/TriangleMesh.hpp | src/libslic3r/TriangleMesh.hpp | ||||||
| src/libslic3r/utils.cpp | src/libslic3r/utils.cpp | ||||||
|  | src/libslic3r/Utils.hpp | ||||||
| src/perlglue.cpp | src/perlglue.cpp | ||||||
| src/poly2tri/common/shapes.cc | src/poly2tri/common/shapes.cc | ||||||
| src/poly2tri/common/shapes.h | src/poly2tri/common/shapes.h | ||||||
|  | @ -200,7 +209,6 @@ xsp/Polygon.xsp | ||||||
| xsp/Polyline.xsp | xsp/Polyline.xsp | ||||||
| xsp/PolylineCollection.xsp | xsp/PolylineCollection.xsp | ||||||
| xsp/Print.xsp | xsp/Print.xsp | ||||||
| xsp/SupportMaterial.xsp |  | ||||||
| xsp/Surface.xsp | xsp/Surface.xsp | ||||||
| xsp/SurfaceCollection.xsp | xsp/SurfaceCollection.xsp | ||||||
| xsp/TriangleMesh.xsp | xsp/TriangleMesh.xsp | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stddef.h> | #include <stddef.h> | ||||||
| #include <boost/predef/detail/endian_compat.h> | #include <boost/detail/endian.hpp> | ||||||
| 
 | 
 | ||||||
| #ifndef BOOST_LITTLE_ENDIAN | #ifndef BOOST_LITTLE_ENDIAN | ||||||
| #error "admesh works correctly on little endian machines only!" | #error "admesh works correctly on little endian machines only!" | ||||||
|  |  | ||||||
|  | @ -43,23 +43,8 @@ stl_open(stl_file *stl, char *file) { | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
| stl_initialize(stl_file *stl) { | stl_initialize(stl_file *stl) { | ||||||
|   stl->error = 0; |   memset(stl, 0, sizeof(stl_file)); | ||||||
|   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; |  | ||||||
|   stl->stats.volume = -1.0; |   stl->stats.volume = -1.0; | ||||||
| 
 |  | ||||||
|   stl->neighbors_start = NULL; |  | ||||||
|   stl->facet_start = NULL; |  | ||||||
|   stl->v_indices = NULL; |  | ||||||
|   stl->v_shared = NULL; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
|  | @ -270,6 +255,7 @@ stl_read(stl_file *stl, int first_facet, int first) { | ||||||
|     rewind(stl->fp); |     rewind(stl->fp); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   char normal_buf[3][32]; | ||||||
|   for(i = first_facet; i < stl->stats.number_of_facets; i++) { |   for(i = first_facet; i < stl->stats.number_of_facets; i++) { | ||||||
|     if(stl->stats.type == binary) |     if(stl->stats.type == binary) | ||||||
|       /* Read a single facet from a binary .STL file */ |       /* 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, "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")
 |       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) + \ |       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, " 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[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[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, " 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, " endloop\n") + | ||||||
|           fscanf(stl->fp, " endfacet\n")) != 12) { |           fscanf(stl->fp, " endfacet\n")) != 12) { | ||||||
|         perror("Something is syntactically very wrong with this ASCII STL!"); |         perror("Something is syntactically very wrong with this ASCII STL!"); | ||||||
|         stl->error = 1; |         stl->error = 1; | ||||||
|         return; |         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 | #if 0 | ||||||
|  |  | ||||||
|  | @ -153,17 +153,35 @@ private: | ||||||
|     PolyNode* GetNextSiblingUp() const; |     PolyNode* GetNextSiblingUp() const; | ||||||
|     void AddChild(PolyNode& child); |     void AddChild(PolyNode& child); | ||||||
|     friend class Clipper; //to access Index
 |     friend class Clipper; //to access Index
 | ||||||
|     friend class ClipperOffset;  |     friend class ClipperOffset; | ||||||
|  |     friend class PolyTree; //to implement the PolyTree::move operator
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class PolyTree: public PolyNode | class PolyTree: public PolyNode | ||||||
| {  | {  | ||||||
| public: | 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; |     PolyNode* GetFirst() const; | ||||||
|     void Clear(); |     void Clear(); | ||||||
|     int Total() const; |     int Total() const; | ||||||
| private: | private: | ||||||
|  |     PolyTree(const PolyTree &src) = delete; | ||||||
|  |     PolyTree& operator=(const PolyTree &src) = delete; | ||||||
|     PolyNodes AllNodes; |     PolyNodes AllNodes; | ||||||
|     friend class Clipper; //to access 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<Point>::overlap(const BoundingBoxBase<Point> &point) const; | ||||||
| template bool BoundingBoxBase<Pointf>::overlap(const BoundingBoxBase<Pointf> &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; |     BoundingBox rotated(double angle, const Point ¢er) const; | ||||||
|     void rotate(double angle) { (*this) = this->rotated(angle); } |     void rotate(double angle) { (*this) = this->rotated(angle); } | ||||||
|     void rotate(double angle, const Point ¢er) { (*this) = this->rotated(angle, center); } |     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() : BoundingBoxBase<Point>() {}; | ||||||
|     BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {}; |     BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {}; | ||||||
|  |  | ||||||
|  | @ -40,13 +40,13 @@ void BridgeDetector::initialize() | ||||||
|     this->angle = -1.; |     this->angle = -1.; | ||||||
| 
 | 
 | ||||||
|     // Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
 |     // 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 possible anchoring edges of this bridging region.
 | ||||||
|     // Detect what edges lie on lower slices by turning bridge contour and holes
 |     // 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.
 |     // 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()).
 |     // 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 |     #ifdef SLIC3R_DEBUG | ||||||
|     printf("  bridge has " PRINTF_ZU " support(s)\n", this->_edges.size()); |     printf("  bridge has " PRINTF_ZU " support(s)\n", this->_edges.size()); | ||||||
|  | @ -117,7 +117,7 @@ BridgeDetector::detect_angle() | ||||||
|         double total_length = 0; |         double total_length = 0; | ||||||
|         double max_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) { |             for (size_t i = 0; i < clipped_lines.size(); ++i) { | ||||||
|                 const Line &line = clipped_lines[i]; |                 const Line &line = clipped_lines[i]; | ||||||
|                 if (expolygons_contain(this->_anchor_regions, line.a) && expolygons_contain(this->_anchor_regions, line.b)) { |                 if (expolygons_contain(this->_anchor_regions, line.a) && expolygons_contain(this->_anchor_regions, line.b)) { | ||||||
|  | @ -203,76 +203,72 @@ std::vector<double> BridgeDetector::bridge_direction_candidates() const | ||||||
|     return angles; |     return angles; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | Polygons BridgeDetector::coverage(double angle) const | ||||||
| BridgeDetector::coverage(double angle, Polygons* coverage) const |  | ||||||
| { | { | ||||||
|     if (angle == -1) angle = this->angle; |     if (angle == -1) | ||||||
|     if (angle == -1) return; |         angle = this->angle; | ||||||
| 
 | 
 | ||||||
|     // 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; |     Polygons covered; | ||||||
|     for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) | 
 | ||||||
|     { |     if (angle != -1) { | ||||||
|         // Clone our expolygon and rotate it so that we work with vertical lines.
 | 
 | ||||||
|         ExPolygon expolygon = *it_expoly; |         // Get anchors, convert them to Polygons and rotate them.
 | ||||||
|         expolygon.rotate(PI/2.0 - angle); |         Polygons anchors = to_polygons(this->_anchor_regions); | ||||||
|  |         polygons_rotate(anchors, PI/2.0 - angle); | ||||||
|          |          | ||||||
|         /*  Outset the bridge expolygon by half the amount we used for detecting anchors;
 |         for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) | ||||||
|             we'll use this one to generate our trapezoids and be sure that their vertices |         { | ||||||
|             are inside the anchors and not on their contours leading to false negatives. */ |             // Clone our expolygon and rotate it so that we work with vertical lines.
 | ||||||
|         ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing)); |             ExPolygon expolygon = *it_expoly; | ||||||
|          |             expolygon.rotate(PI/2.0 - angle); | ||||||
|         // Compute trapezoids according to a vertical orientation
 |              | ||||||
|         Polygons trapezoids; |             /*  Outset the bridge expolygon by half the amount we used for detecting anchors;
 | ||||||
|         for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) |                 we'll use this one to generate our trapezoids and be sure that their vertices | ||||||
|             it->get_trapezoids2(&trapezoids, PI/2.0); |                 are inside the anchors and not on their contours leading to false negatives. */ | ||||||
|          |             ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing)); | ||||||
|         for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) { |              | ||||||
|             Lines supported = intersection(trapezoid->lines(), anchors); |             // Compute trapezoids according to a vertical orientation
 | ||||||
|             size_t n_supported = 0; |             Polygons trapezoids; | ||||||
|             // not nice, we need a more robust non-numeric check
 |             for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) | ||||||
|             for (size_t i = 0; i < supported.size(); ++i) |                 it->get_trapezoids2(&trapezoids, PI/2.0); | ||||||
|                 if (supported[i].length() >= this->spacing) |              | ||||||
|                     ++ n_supported; |             for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) { | ||||||
|             if (n_supported >= 2)  |                 Lines supported = intersection_ln(trapezoid->lines(), anchors); | ||||||
|                 covered.push_back(STDMOVE(*trapezoid)); |                 size_t n_supported = 0; | ||||||
|  |                 // not nice, we need a more robust non-numeric check
 | ||||||
|  |                 for (size_t i = 0; i < supported.size(); ++i) | ||||||
|  |                     if (supported[i].length() >= this->spacing) | ||||||
|  |                         ++ n_supported; | ||||||
|  |                 if (n_supported >= 2)  | ||||||
|  |                     covered.push_back(STDMOVE(*trapezoid)); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
 | ||||||
|  |         // instead of exact overlaps.
 | ||||||
|  |         covered = union_(covered); | ||||||
|  | 
 | ||||||
|  |         // Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
 | ||||||
|  |         polygons_rotate(covered, -(PI/2.0 - angle)); | ||||||
|  |     	covered = intersection(covered, to_polygons(this->expolygons)); | ||||||
|  | 
 | ||||||
|  |         /*
 | ||||||
|  |         if (0) { | ||||||
|  |             my @lines = map @{$_->lines}, @$trapezoids; | ||||||
|  |             $_->rotate(-(PI/2 - $angle), [0,0]) for @lines; | ||||||
|  |              | ||||||
|  |             require "Slic3r/SVG.pm"; | ||||||
|  |             Slic3r::SVG::output( | ||||||
|  |                 "coverage_" . rad2deg($angle) . ".svg", | ||||||
|  |                 expolygons          => [$self->expolygon], | ||||||
|  |                 green_expolygons    => $self->_anchor_regions, | ||||||
|  |                 red_expolygons      => $coverage, | ||||||
|  |                 lines               => \@lines, | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         */ | ||||||
|     } |     } | ||||||
| 
 |     return covered; | ||||||
|     // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
 |  | ||||||
|     // instead of exact overlaps.
 |  | ||||||
|     covered = union_(covered); |  | ||||||
| 
 |  | ||||||
|     // 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); |  | ||||||
| 
 |  | ||||||
|     /*
 |  | ||||||
|     if (0) { |  | ||||||
|         my @lines = map @{$_->lines}, @$trapezoids; |  | ||||||
|         $_->rotate(-(PI/2 - $angle), [0,0]) for @lines; |  | ||||||
|          |  | ||||||
|         require "Slic3r/SVG.pm"; |  | ||||||
|         Slic3r::SVG::output( |  | ||||||
|             "coverage_" . rad2deg($angle) . ".svg", |  | ||||||
|             expolygons          => [$self->expolygon], |  | ||||||
|             green_expolygons    => $self->_anchor_regions, |  | ||||||
|             red_expolygons      => $coverage, |  | ||||||
|             lines               => \@lines, |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     */ |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Polygons |  | ||||||
| BridgeDetector::coverage(double angle) const |  | ||||||
| { |  | ||||||
|     Polygons pp; |  | ||||||
|     this->coverage(angle, &pp); |  | ||||||
|     return pp; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /*  This method returns the bridge edges (as polylines) that are not supported
 | /*  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) {     |     for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) {     | ||||||
|         // get unsupported bridge edges (both contour and holes)
 |         // get unsupported bridge edges (both contour and holes)
 | ||||||
|         Polylines unuspported_polylines; |         Lines unsupported_lines = to_lines(diff_pl(to_polylines(*it_expoly), grown_lower)); | ||||||
|         diff(to_polylines(*it_expoly), grown_lower, &unuspported_polylines); |  | ||||||
|         Lines unsupported_lines = to_lines(unuspported_polylines); |  | ||||||
|         /*  Split into individual segments and filter out edges parallel to the bridging angle
 |         /*  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, |             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 |             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(ExPolygon _expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); | ||||||
|     BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); |     BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); | ||||||
|     bool detect_angle(); |     bool detect_angle(); | ||||||
|     void coverage(double angle, Polygons* coverage) const; |  | ||||||
|     Polygons coverage(double angle = -1) const; |     Polygons coverage(double angle = -1) const; | ||||||
|     void unsupported_edges(double angle, Polylines* unsupported) const; |     void unsupported_edges(double angle, Polylines* unsupported) const; | ||||||
|     Polylines unsupported_edges(double angle = -1) 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 { | 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
 | // legacy code from Clipper documentation
 | ||||||
| void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons); | void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons); | ||||||
| void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons); | void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons); | ||||||
| //-----------------------------------------------------------
 | //-----------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
| void Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output); | ClipperLib::Path   Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input); | ||||||
| template <class T> | ClipperLib::Paths  Slic3rMultiPoints_to_ClipperPaths(const Polygons &input); | ||||||
| void Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output); | ClipperLib::Paths  Slic3rMultiPoints_to_ClipperPaths(const Polylines &input); | ||||||
| template <class T> | Slic3r::Polygon    ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input); | ||||||
| void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output); | Slic3r::Polyline   ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input); | ||||||
| template <class T> | Slic3r::Polygons   ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input); | ||||||
| void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output); | Slic3r::Polylines  ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input); | ||||||
| void ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output); | Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input); | ||||||
| 
 |  | ||||||
| void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale); |  | ||||||
| 
 | 
 | ||||||
| // offset Polygons
 | // offset Polygons
 | ||||||
| void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta, | ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); | ||||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit); | ||||||
|     double miterLimit = 3); | inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  double miterLimit = 3) | ||||||
| void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta, |     { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } | ||||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) | ||||||
|     double miterLimit = 3); |     { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); } | ||||||
| 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); |  | ||||||
| 
 | 
 | ||||||
| // offset Polylines
 | // offset Polylines
 | ||||||
| void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta, | inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) | ||||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,  |     { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } | ||||||
|     double miterLimit = 3); | inline Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3) | ||||||
| void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta, |     { return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); } | ||||||
|     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); |  | ||||||
| 
 | 
 | ||||||
| void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta, | // offset expolygons and surfaces
 | ||||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  | ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit); | ||||||
|     double miterLimit = 3); | ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit); | ||||||
| Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, | inline Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3) | ||||||
|     double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,  |     { return ClipperPaths_to_Slic3rPolygons(_offset(expolygon, delta, joinType, miterLimit)); } | ||||||
|     double miterLimit = 3); | 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, | ClipperLib::Paths _offset2(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); |  | ||||||
| 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,  |  | ||||||
|     double miterLimit = 3); |     double miterLimit = 3); | ||||||
| Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1, | Slic3r::Polygons offset2(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); |  | ||||||
| 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,  |  | ||||||
|     double miterLimit = 3); |     double miterLimit = 3); | ||||||
| Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1, | 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); |     double miterLimit = 3); | ||||||
| 
 | 
 | ||||||
| template <class T> | Slic3r::Polygons _clipper(ClipperLib::ClipType clipType, | ||||||
| void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,  |     const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||||
|     const Slic3r::Polygons &clip, T* retval, bool safety_offset_); | Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType, | ||||||
| void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,  |     const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||||
|     const Slic3r::Polygons &clip, ClipperLib::Paths* retval, bool safety_offset_); | Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, | ||||||
| void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,  |     const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||||
|     const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_); | Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, | ||||||
| void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,  |     const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||||
|     const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); | Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, | ||||||
| void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,  |     const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | ||||||
|     const Slic3r::Polygons &clip, Slic3r::Polylines* retval); |  | ||||||
| void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject,  |  | ||||||
|     const Slic3r::Polygons &clip, Slic3r::Lines* retval); |  | ||||||
| 
 | 
 | ||||||
| template <class SubjectType, class ResultType> | // diff
 | ||||||
| void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); | 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> | inline Slic3r::ExPolygons | ||||||
| void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_ = false); | 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> | inline Slic3r::Polygons | ||||||
| Slic3r::ExPolygons diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_ = false); | 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> | inline Slic3r::Polylines | ||||||
| void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); | 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> | inline Slic3r::Polylines | ||||||
| SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | 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 | inline Slic3r::Lines | ||||||
| intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | 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> | // intersection
 | ||||||
| bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); | 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,  | inline Slic3r::ExPolygons | ||||||
|     bool safety_offset_ = false); | 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> | inline Slic3r::ExPolygons | ||||||
| void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_ = false); | 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); | inline Slic3r::Polygons | ||||||
| Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset = false); | intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) | ||||||
| Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, 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); | inline Slic3r::Polylines | ||||||
| Slic3r::Polygons union_(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset = false); | 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); | inline Slic3r::Polylines | ||||||
| void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_ = false); | intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) | ||||||
| static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval); | { | ||||||
|  |     return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear = false); | inline Slic3r::Lines | ||||||
| void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear = false); | 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); | void safety_offset(ClipperLib::Paths* paths); | ||||||
| 
 | 
 | ||||||
|  | Polygons top_level_islands(const Slic3r::Polygons &polygons); | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| #ifndef slic3r_Config_hpp_ | #ifndef slic3r_Config_hpp_ | ||||||
| #define slic3r_Config_hpp_ | #define slic3r_Config_hpp_ | ||||||
| 
 | 
 | ||||||
|  | #include <assert.h> | ||||||
| #include <map> | #include <map> | ||||||
| #include <climits> | #include <climits> | ||||||
| #include <cstdio> | #include <cstdio> | ||||||
|  | @ -73,11 +74,8 @@ class ConfigOptionVector : public ConfigOptionVectorBase | ||||||
|     }; |     }; | ||||||
|      |      | ||||||
|     T get_at(size_t i) const { |     T get_at(size_t i) const { | ||||||
|         try { |         assert(! this->values.empty()); | ||||||
|             return this->values.at(i); |         return (i < this->values.size()) ? this->values[i] : this->values.front(); | ||||||
|         } catch (const std::out_of_range& oor) { |  | ||||||
|             return this->values.front(); |  | ||||||
|         } |  | ||||||
|     }; |     }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <float.h> | #include <float.h> | ||||||
|  | #include <unordered_map> | ||||||
| 
 | 
 | ||||||
| #ifdef SLIC3R_GUI | #ifdef SLIC3R_GUI | ||||||
| #include <wx/image.h> | #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) | void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) | ||||||
| { | { | ||||||
| 	// 1) Measure the bounding box.
 | 	// 1) Measure the bounding box.
 | ||||||
| 	m_bbox.defined = false; |  | ||||||
| 	for (size_t i = 0; i < m_contours.size(); ++ i) { | 	for (size_t i = 0; i < m_contours.size(); ++ i) { | ||||||
| 		const Slic3r::Points &pts = *m_contours[i]; | 		const Slic3r::Points &pts = *m_contours[i]; | ||||||
| 		for (size_t j = 0; j < pts.size(); ++ j) | 		for (size_t j = 0; j < pts.size(); ++ j) | ||||||
|  | @ -839,6 +839,8 @@ void EdgeGrid::Grid::calculate_sdf() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| #if 0 | #if 0 | ||||||
|  | 	static int iRun = 0; | ||||||
|  | 	++ iRun; | ||||||
| //#ifdef SLIC3R_GUI
 | //#ifdef SLIC3R_GUI
 | ||||||
| 	{  | 	{  | ||||||
| 		wxImage img(ncols, nrows); | 		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); | 		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 */ | #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 */ | #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 */ | #endif /* SLIC3R_GUI */ | ||||||
| } | } | ||||||
|  | @ -1118,7 +1120,7 @@ float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const | ||||||
| 
 | 
 | ||||||
| 	return f; | 	return f; | ||||||
| } | } | ||||||
| 
 |   | ||||||
| bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const { | bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const { | ||||||
| 	BoundingBox bbox; | 	BoundingBox bbox; | ||||||
| 	bbox.min = bbox.max = Point(pt.x - m_bbox.min.x, pt.y - m_bbox.min.y); | 	bbox.min = bbox.max = Point(pt.x - m_bbox.min.x, pt.y - m_bbox.min.y); | ||||||
|  | @ -1222,6 +1224,135 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo | ||||||
| 	return true; | 	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 | #ifdef SLIC3R_GUI | ||||||
| void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path) | 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; | 	++iRun; | ||||||
|      |      | ||||||
|     const coord_t search_radius = grid.resolution() * 2; |     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 r = 0; r < h; ++r) { | ||||||
|     	for (coord_t c = 0; c < w; ++ c) { |     	for (coord_t c = 0; c < w; ++ c) { | ||||||
| 			unsigned char *pxl = data + (((h - r - 1) * w) + c) * 3; | 			unsigned char *pxl = data + (((h - r - 1) * w) + c) * 3; | ||||||
| 			Point pt(c * resolution + bbox.min.x, r * resolution + bbox.min.y); | 			Point pt(c * resolution + bbox.min.x, r * resolution + bbox.min.y); | ||||||
| 			coordf_t min_dist; | 			coordf_t min_dist; | ||||||
| 			bool on_segment; | 			bool on_segment = true; | ||||||
| //			if (grid.signed_distance_edges(pt, search_radius, min_dist, &on_segment)) {
 | 			#if 0 | ||||||
|  | 			if (grid.signed_distance_edges(pt, search_radius, min_dist, &on_segment)) { | ||||||
|  | 			#else | ||||||
| 			if (grid.signed_distance(pt, search_radius, min_dist)) { | 			if (grid.signed_distance(pt, search_radius, min_dist)) { | ||||||
| 				//FIXME
 | 			#endif | ||||||
| 				on_segment = true; |  | ||||||
| 				float s = 255 * std::abs(min_dist) / float(display_blend_radius); | 				float s = 255 * std::abs(min_dist) / float(display_blend_radius); | ||||||
| 				int is = std::max(0, std::min(255, int(floor(s + 0.5f)))); | 				int is = std::max(0, std::min(255, int(floor(s + 0.5f)))); | ||||||
| 				if (min_dist < 0) { | 				if (min_dist < 0) { | ||||||
|  | @ -1254,9 +1386,9 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo | ||||||
| 						pxl[1] = 255 - is; | 						pxl[1] = 255 - is; | ||||||
| 						pxl[2] = 255 - is; | 						pxl[2] = 255 - is; | ||||||
| 					} else { | 					} else { | ||||||
| 						pxl[0] = 128; | 						pxl[0] = 255; | ||||||
| 						pxl[1] = 128; | 						pxl[1] = 0; | ||||||
| 						pxl[2] = 255 - is;						 | 						pxl[2] = 255 - is; | ||||||
| 					} | 					} | ||||||
| 				} | 				} | ||||||
| 				else { | 				else { | ||||||
|  |  | ||||||
|  | @ -12,11 +12,14 @@ | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| namespace EdgeGrid { | namespace EdgeGrid { | ||||||
| 
 | 
 | ||||||
| struct Grid | class Grid | ||||||
| { | { | ||||||
|  | public: | ||||||
| 	Grid(); | 	Grid(); | ||||||
| 	~Grid(); | 	~Grid(); | ||||||
| 
 | 
 | ||||||
|  | 	void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; } | ||||||
|  | 
 | ||||||
| 	void create(const Polygons &polygons, coord_t resolution); | 	void create(const Polygons &polygons, coord_t resolution); | ||||||
| 	void create(const ExPolygon &expoly, coord_t resolution); | 	void create(const ExPolygon &expoly, coord_t resolution); | ||||||
| 	void create(const ExPolygons &expolygons, 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		rows() const { return m_rows; } | ||||||
| 	const size_t		cols() const { return m_cols; } | 	const size_t		cols() const { return m_cols; } | ||||||
| 
 | 
 | ||||||
|  | 	// For supports: Contours enclosing the rasterized edges.
 | ||||||
|  | 	Polygons 			contours_simplified(coord_t offset) const; | ||||||
|  | 
 | ||||||
| protected: | protected: | ||||||
| 	struct Cell { | 	struct Cell { | ||||||
| 		Cell() : begin(0), end(0) {} | 		Cell() : begin(0), end(0) {} | ||||||
|  | @ -65,6 +71,18 @@ protected: | ||||||
| #if 0 | #if 0 | ||||||
| 	bool line_cell_intersect(const Point &p1, const Point &p2, const Cell &cell); | 	bool line_cell_intersect(const Point &p1, const Point &p2, const Cell &cell); | ||||||
| #endif | #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.
 | 	// Bounding box around the contours.
 | ||||||
| 	BoundingBox 								m_bbox; | 	BoundingBox 								m_bbox; | ||||||
|  |  | ||||||
|  | @ -99,9 +99,7 @@ ExPolygon::contains(const Line &line) const | ||||||
| bool | bool | ||||||
| ExPolygon::contains(const Polyline &polyline) const | ExPolygon::contains(const Polyline &polyline) const | ||||||
| { | { | ||||||
|     Polylines pl_out; |     return diff_pl((Polylines)polyline, *this).empty(); | ||||||
|     diff((Polylines)polyline, *this, &pl_out); |  | ||||||
|     return pl_out.empty(); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool | bool | ||||||
|  | @ -115,8 +113,7 @@ ExPolygon::contains(const Polylines &polylines) const | ||||||
|     svg.draw_outline(*this); |     svg.draw_outline(*this); | ||||||
|     svg.draw(polylines, "blue"); |     svg.draw(polylines, "blue"); | ||||||
|     #endif |     #endif | ||||||
|     Polylines pl_out; |     Polylines pl_out = diff_pl(polylines, *this); | ||||||
|     diff(polylines, *this, &pl_out); |  | ||||||
|     #if 0 |     #if 0 | ||||||
|     svg.draw(pl_out, "red"); |     svg.draw(pl_out, "red"); | ||||||
|     #endif |     #endif | ||||||
|  | @ -162,8 +159,7 @@ ExPolygon::overlaps(const ExPolygon &other) const | ||||||
|     svg.draw_outline(*this); |     svg.draw_outline(*this); | ||||||
|     svg.draw_outline(other, "blue"); |     svg.draw_outline(other, "blue"); | ||||||
|     #endif |     #endif | ||||||
|     Polylines pl_out; |     Polylines pl_out = intersection_pl((Polylines)other, *this); | ||||||
|     intersection((Polylines)other, *this, &pl_out); |  | ||||||
|     #if 0 |     #if 0 | ||||||
|     svg.draw(pl_out, "red"); |     svg.draw(pl_out, "red"); | ||||||
|     #endif |     #endif | ||||||
|  | @ -396,11 +392,8 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const | ||||||
|         poly[3].y = bb.max.y; |         poly[3].y = bb.max.y; | ||||||
|          |          | ||||||
|         // intersect with this expolygon
 |         // intersect with this expolygon
 | ||||||
|         Polygons trapezoids; |  | ||||||
|         intersection<Polygons,Polygons>(poly, *this, &trapezoids); |  | ||||||
|          |  | ||||||
|         // append results to return value
 |         // 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
 |     // convert polygons
 | ||||||
|     std::list<TPPLPoly> input; |     std::list<TPPLPoly> input; | ||||||
|      |      | ||||||
|     Polygons pp = *this; |     ExPolygons expp = union_ex(simplify_polygons(to_polygons(*this), true)); | ||||||
|     simplify_polygons(pp, &pp, true); |  | ||||||
|     ExPolygons expp; |  | ||||||
|     union_(pp, &expp); |  | ||||||
|      |      | ||||||
|     for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { |     for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { | ||||||
|         // contour
 |         // contour
 | ||||||
|         { |         { | ||||||
|             TPPLPoly p; |             TPPLPoly p; | ||||||
|             p.Init(ex->contour.points.size()); |             p.Init(int(ex->contour.points.size())); | ||||||
|             //printf(PRINTF_ZU "\n0\n", 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) { |             for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) { | ||||||
|                 p[ point-ex->contour.points.begin() ].x = point->x; |                 p[ point-ex->contour.points.begin() ].x = point->x; | ||||||
|  | @ -480,8 +470,8 @@ ExPolygon::triangulate_pp(Polygons* polygons) const | ||||||
|         Polygon p; |         Polygon p; | ||||||
|         p.points.resize(num_points); |         p.points.resize(num_points); | ||||||
|         for (long i = 0; i < num_points; ++i) { |         for (long i = 0; i < num_points; ++i) { | ||||||
|             p.points[i].x = (*poly)[i].x; |             p.points[i].x = coord_t((*poly)[i].x); | ||||||
|             p.points[i].y = (*poly)[i].y; |             p.points[i].y = coord_t((*poly)[i].y); | ||||||
|         } |         } | ||||||
|         polygons->push_back(p); |         polygons->push_back(p); | ||||||
|     } |     } | ||||||
|  | @ -490,8 +480,7 @@ ExPolygon::triangulate_pp(Polygons* polygons) const | ||||||
| void | void | ||||||
| ExPolygon::triangulate_p2t(Polygons* polygons) const | ExPolygon::triangulate_p2t(Polygons* polygons) const | ||||||
| { | { | ||||||
|     ExPolygons expp; |     ExPolygons expp = simplify_polygons_ex(*this, true); | ||||||
|     simplify_polygons(*this, &expp, true); |  | ||||||
|      |      | ||||||
|     for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { |     for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { | ||||||
|         // TODO: prevent duplicate points
 |         // TODO: prevent duplicate points
 | ||||||
|  |  | ||||||
|  | @ -13,9 +13,17 @@ typedef std::vector<ExPolygon> ExPolygons; | ||||||
| 
 | 
 | ||||||
| class ExPolygon | 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; |     Polygon contour; | ||||||
|     Polygons holes; |     Polygons holes; | ||||||
|  | 
 | ||||||
|     operator Points() const; |     operator Points() const; | ||||||
|     operator Polygons() const; |     operator Polygons() const; | ||||||
|     operator Polylines() const; |     operator Polylines() const; | ||||||
|  | @ -253,6 +261,18 @@ inline void polygons_append(Polygons &dst, ExPolygons &&src) | ||||||
| } | } | ||||||
| #endif | #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) | inline void expolygons_rotate(ExPolygons &expolys, double angle) | ||||||
| { | { | ||||||
|     for (ExPolygons::iterator p = expolys.begin(); p != expolys.end(); ++p) |     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> | #include <boost/polygon/polygon.hpp> | ||||||
| namespace boost { namespace polygon { | namespace boost { namespace polygon { | ||||||
|     template <> |     template <> | ||||||
|         struct polygon_traits<ExPolygon> { |         struct polygon_traits<Slic3r::ExPolygon> { | ||||||
|         typedef coord_t coordinate_type; |         typedef coord_t coordinate_type; | ||||||
|         typedef Points::const_iterator iterator_type; |         typedef Slic3r::Points::const_iterator iterator_type; | ||||||
|         typedef Point point_type; |         typedef Slic3r::Point point_type; | ||||||
| 
 | 
 | ||||||
|         // Get the begin iterator
 |         // 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(); |             return t.contour.points.begin(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Get the end iterator
 |         // 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(); |             return t.contour.points.end(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Get the number of sides of the polygon
 |         // 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(); |             return t.contour.points.size(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Get the winding direction of the polygon
 |         // 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; |             return unknown_winding; | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     template <> |     template <> | ||||||
|     struct polygon_mutable_traits<ExPolygon> { |     struct polygon_mutable_traits<Slic3r::ExPolygon> { | ||||||
|         //expects stl style iterators
 |         //expects stl style iterators
 | ||||||
|         template <typename iT> |         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); |             expolygon.contour.points.assign(input_begin, input_end); | ||||||
|             // skip last point since Boost will set last point = first point
 |             // skip last point since Boost will set last point = first point
 | ||||||
|             expolygon.contour.points.pop_back(); |             expolygon.contour.points.pop_back(); | ||||||
|  | @ -321,27 +341,27 @@ namespace boost { namespace polygon { | ||||||
|      |      | ||||||
|      |      | ||||||
|     template <> |     template <> | ||||||
|     struct geometry_concept<ExPolygon> { typedef polygon_with_holes_concept type; }; |     struct geometry_concept<Slic3r::ExPolygon> { typedef polygon_with_holes_concept type; }; | ||||||
| 
 | 
 | ||||||
|     template <> |     template <> | ||||||
|     struct polygon_with_holes_traits<ExPolygon> { |     struct polygon_with_holes_traits<Slic3r::ExPolygon> { | ||||||
|         typedef Polygons::const_iterator iterator_holes_type; |         typedef Slic3r::Polygons::const_iterator iterator_holes_type; | ||||||
|         typedef Polygon hole_type; |         typedef Slic3r::Polygon hole_type; | ||||||
|         static inline iterator_holes_type begin_holes(const ExPolygon& t) { |         static inline iterator_holes_type begin_holes(const Slic3r::ExPolygon& t) { | ||||||
|             return t.holes.begin(); |             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(); |             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(); |             return (int)t.holes.size(); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     template <> |     template <> | ||||||
|     struct polygon_with_holes_mutable_traits<ExPolygon> { |     struct polygon_with_holes_mutable_traits<Slic3r::ExPolygon> { | ||||||
|          template <typename iT> |          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); |               t.holes.assign(inputBegin, inputEnd); | ||||||
|               return t; |               return t; | ||||||
|          } |          } | ||||||
|  | @ -349,32 +369,32 @@ namespace boost { namespace polygon { | ||||||
|      |      | ||||||
|     //first we register CPolygonSet as a polygon set
 |     //first we register CPolygonSet as a polygon set
 | ||||||
|     template <> |     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
 |     //next we map to the concept through traits
 | ||||||
|     template <> |     template <> | ||||||
|     struct polygon_set_traits<ExPolygons> { |     struct polygon_set_traits<Slic3r::ExPolygons> { | ||||||
|         typedef coord_t coordinate_type; |         typedef coord_t coordinate_type; | ||||||
|         typedef ExPolygons::const_iterator iterator_type; |         typedef Slic3r::ExPolygons::const_iterator iterator_type; | ||||||
|         typedef ExPolygons operator_arg_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(); |             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(); |             return polygon_set.end(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         //don't worry about these, just return false from them
 |         //don't worry about these, just return false from them
 | ||||||
|         static inline bool clean(const ExPolygons& polygon_set) { return false; } |         static inline bool clean(const Slic3r::ExPolygons& polygon_set) { return false; } | ||||||
|         static inline bool sorted(const ExPolygons& polygon_set) { return false; } |         static inline bool sorted(const Slic3r::ExPolygons& polygon_set) { return false; } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     template <> |     template <> | ||||||
|     struct polygon_set_mutable_traits<ExPolygons> { |     struct polygon_set_mutable_traits<Slic3r::ExPolygons> { | ||||||
|         template <typename input_iterator_type> |         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); |             expolygons.assign(input_begin, input_end); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  | @ -13,19 +13,13 @@ namespace Slic3r { | ||||||
| void | void | ||||||
| ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const | ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const | ||||||
| { | { | ||||||
|     // perform clipping
 |     this->_inflate_collection(intersection_pl(this->polyline, collection), retval); | ||||||
|     Polylines clipped; |  | ||||||
|     intersection<Polylines,Polylines>(this->polyline, collection, &clipped); |  | ||||||
|     return this->_inflate_collection(clipped, retval); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
| ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const | ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const | ||||||
| { | { | ||||||
|     // perform clipping
 |     this->_inflate_collection(diff_pl(this->polyline, collection), retval); | ||||||
|     Polylines clipped; |  | ||||||
|     diff<Polylines,Polylines>(this->polyline, collection, &clipped); |  | ||||||
|     return this->_inflate_collection(clipped, retval); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | 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 | void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const | ||||||
| { | { | ||||||
|     Polygons tmp; |     polygons_append(out, offset(this->polyline, float(scale_(this->width/2)) + scaled_epsilon)); | ||||||
|     offset(this->polyline, &tmp, scale_(this->width/2) + scaled_epsilon); |  | ||||||
|     polygons_append(out, STDMOVE(tmp)); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const | 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.
 |     // 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.
 |     // 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()); |     Flow flow(this->width, this->height, 0.f, this->is_bridge()); | ||||||
|     Polygons tmp; |     polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); | ||||||
|     offset(this->polyline, &tmp, 0.5f * flow.scaled_spacing() + scaled_epsilon); |  | ||||||
|     polygons_append(out, STDMOVE(tmp)); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool | bool | ||||||
|  |  | ||||||
|  | @ -69,11 +69,11 @@ class ExtrusionPath : public ExtrusionEntity | ||||||
| public: | public: | ||||||
|     Polyline polyline; |     Polyline polyline; | ||||||
|     ExtrusionRole role; |     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; |     double mm3_per_mm; | ||||||
|     // Width of the extrusion.
 |     // Width of the extrusion, used for visualization purposes.
 | ||||||
|     float width; |     float width; | ||||||
|     // Height of the extrusion.
 |     // Height of the extrusion, used for visualization purposed.
 | ||||||
|     float height; |     float height; | ||||||
|      |      | ||||||
|     ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {}; |     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(); } |     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 | #endif | ||||||
|  |  | ||||||
|  | @ -803,7 +803,7 @@ void gcode_spread_points( | ||||||
| 				const Cell &cell = cells[i]; | 				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; | 				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.
 | 			// The volume under the circle does not fit.
 | ||||||
| 			// 1) Fill the underfilled cells and remove them from the list.
 | 			// 1) Fill the underfilled cells and remove them from the list.
 | ||||||
| 			float volume_borrowed_total = 0.; | 			float volume_borrowed_total = 0.; | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ enum ExtrusionSimulationType | ||||||
|     ExtrusionSimulationDontSpread, |     ExtrusionSimulationDontSpread, | ||||||
|     ExtrisopmSimulationSpreadNotOverfilled, |     ExtrisopmSimulationSpreadNotOverfilled, | ||||||
|     ExtrusionSimulationSpreadFull, |     ExtrusionSimulationSpreadFull, | ||||||
|     ExtrusionSimulationSpreadExcess, |     ExtrusionSimulationSpreadExcess | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // An opaque class, to keep the boost stuff away from the header.
 | // An opaque class, to keep the boost stuff away from the header.
 | ||||||
|  |  | ||||||
|  | @ -246,22 +246,21 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) | ||||||
|             flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow()); |             flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow()); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // save into layer
 |         // Save into layer.
 | ||||||
|         { |         auto *eec = new ExtrusionEntityCollection(); | ||||||
|             ExtrusionRole role = is_bridge ? erBridgeInfill : |         out.entities.push_back(eec); | ||||||
|                 (surface.is_solid() ? ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) : erInternalInfill); |         // Only concentric fills are not sorted.
 | ||||||
|             ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection()); |         eec->no_sort = f->no_sort(); | ||||||
|             out.entities.push_back(&collection); |         extrusion_entities_append_paths( | ||||||
|             // Only concentric fills are not sorted.
 |             eec->entities, STDMOVE(polylines), | ||||||
|             collection.no_sort = f->no_sort(); |             is_bridge ? | ||||||
|             for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) { |                 erBridgeInfill : | ||||||
|                 ExtrusionPath *path = new ExtrusionPath(role, flow.mm3_per_mm(), flow.width, flow.height); |                 (surface.is_solid() ? | ||||||
|                 collection.entities.push_back(path); |                     ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) : | ||||||
|                 path->polyline.points.swap(it->points); |                     erInternalInfill), | ||||||
|             } |             flow.mm3_per_mm(), flow.width, flow.height); | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|      | 
 | ||||||
|     // add thin fill regions
 |     // add thin fill regions
 | ||||||
|     // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
 |     // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
 | ||||||
|     // Unpacks the collection, creates multiple collections per path.
 |     // Unpacks the collection, creates multiple collections per path.
 | ||||||
|  |  | ||||||
|  | @ -168,7 +168,7 @@ void Fill3DHoneycomb::_fill_surface_single( | ||||||
|         it->translate(bb.min.x, bb.min.y); |         it->translate(bb.min.x, bb.min.y); | ||||||
| 
 | 
 | ||||||
|     // clip pattern to boundaries
 |     // clip pattern to boundaries
 | ||||||
|     intersection(polylines, (Polygons)expolygon, &polylines); |     polylines = intersection_pl(polylines, (Polygons)expolygon); | ||||||
| 
 | 
 | ||||||
|     // connect lines
 |     // connect lines
 | ||||||
|     if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
 |     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) | Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms) | ||||||
| { | { | ||||||
|     // Perform offset.
 |     // Perform offset.
 | ||||||
|     Slic3r::ExPolygons expp; |     Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(-0.5*scale_(this->spacing))); | ||||||
|     offset(surface->expolygon, &expp, -0.5*scale_(this->spacing)); |  | ||||||
|     // Create the infills for each of the regions.
 |     // Create the infills for each of the regions.
 | ||||||
|     Polylines polylines_out; |     Polylines polylines_out; | ||||||
|     for (size_t i = 0; i < expp.size(); ++ i) |     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
 |     // generate paths from the outermost to the innermost, to avoid
 | ||||||
|     // adhesion problems of the first central tiny loops
 |     // 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
 |     // split paths using a nearest neighbor search
 | ||||||
|     size_t iPathFirst = polylines_out.size(); |     size_t iPathFirst = polylines_out.size(); | ||||||
|  |  | ||||||
|  | @ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single( | ||||||
|             Polylines p; |             Polylines p; | ||||||
|             for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it) |             for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it) | ||||||
|                 p.push_back((Polyline)(*it)); |                 p.push_back((Polyline)(*it)); | ||||||
|             intersection(p, (Polygons)expolygon, &paths); |             paths = intersection_pl(p, to_polygons(expolygon)); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // connect paths
 |         // connect paths
 | ||||||
|  | @ -122,7 +122,7 @@ void FillHoneycomb::_fill_surface_single( | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         // clip paths again to prevent connection segments from crossing the expolygon boundaries
 |         // 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.
 |         // Move the polylines to the output, avoid a deep copy.
 | ||||||
|         size_t j = polylines_out.size(); |         size_t j = polylines_out.size(); | ||||||
|         polylines_out.resize(j + paths.size(), Polyline()); |         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->x * distance_between_lines + 0.5)),  | ||||||
|                 coord_t(floor(it->y * 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_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
 | ||||||
|         intersection(polylines, (Polygons)expolygon, &polylines); |         polylines = intersection_pl(polylines, to_polygons(expolygon)); | ||||||
| 
 | 
 | ||||||
| /*        
 | /*        
 | ||||||
|         if (1) { |         if (1) { | ||||||
|  |  | ||||||
|  | @ -63,7 +63,7 @@ void FillRectilinear::_fill_surface_single( | ||||||
|         pts.push_back(it->a); |         pts.push_back(it->a); | ||||||
|         pts.push_back(it->b); |         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!
 |     // FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
 | ||||||
|     const float INFILL_OVERLAP_OVER_SPACING = 0.3f; |     const float INFILL_OVERLAP_OVER_SPACING = 0.3f; | ||||||
|  |  | ||||||
|  | @ -372,11 +372,9 @@ public: | ||||||
|         bool sticks_removed = remove_sticks(polygons_src); |         bool sticks_removed = remove_sticks(polygons_src); | ||||||
| //        if (sticks_removed) printf("Sticks removed!\n");
 | //        if (sticks_removed) printf("Sticks removed!\n");
 | ||||||
|         polygons_outer = offset(polygons_src, aoffset1, |         polygons_outer = offset(polygons_src, aoffset1, | ||||||
|             CLIPPER_OFFSET_SCALE, |  | ||||||
|             ClipperLib::jtMiter, |             ClipperLib::jtMiter, | ||||||
|             mitterLimit); |             mitterLimit); | ||||||
|         polygons_inner = offset(polygons_outer, aoffset2 - aoffset1, |         polygons_inner = offset(polygons_outer, aoffset2 - aoffset1, | ||||||
|             CLIPPER_OFFSET_SCALE, |  | ||||||
|             ClipperLib::jtMiter, |             ClipperLib::jtMiter, | ||||||
|             mitterLimit); |             mitterLimit); | ||||||
| 		// Filter out contours with zero area or small area, contours with 2 points only.
 | 		// 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); |         Point refpt = rotate_vector.second.rotated(- rotate_vector.first); | ||||||
|         // _align_to_grid will not work correctly with positive pattern_shift.
 |         // _align_to_grid will not work correctly with positive pattern_shift.
 | ||||||
|         coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing; |         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.merge(_align_to_grid( | ||||||
|             bounding_box.min,  |             bounding_box.min,  | ||||||
|             Point(line_spacing, line_spacing),  |             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.
 |     // Intersect a set of euqally spaced vertical lines wiht expolygon.
 | ||||||
|     // n_vlines = ceil(bbox_width / line_spacing)
 |     // n_vlines = ceil(bbox_width / line_spacing)
 | ||||||
|     size_t  n_vlines = (bounding_box.max.x - bounding_box.min.x + line_spacing - 1) / 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 | #ifdef SLIC3R_DEBUG | ||||||
|     static int iRun = 0; |     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 
 | /* This method returns the centerline spacing between two adjacent extrusions 
 | ||||||
|    having the same extrusion width (and other properties). */ |    having the same extrusion width (and other properties). */ | ||||||
| float | float | ||||||
| Flow::spacing() const { | Flow::spacing() const  | ||||||
|     if (this->bridge) { | { | ||||||
|  | #ifdef HAS_PERIMETER_LINE_OVERLAP | ||||||
|  |     if (this->bridge) | ||||||
|         return this->width + BRIDGE_EXTRA_SPACING; |         return this->width + BRIDGE_EXTRA_SPACING; | ||||||
|     } |  | ||||||
|      |  | ||||||
|     // rectangle with semicircles at the ends
 |     // rectangle with semicircles at the ends
 | ||||||
|     float min_flow_spacing = this->width - this->height * (1 - PI/4.0); |     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
 | /* This method returns the centerline spacing between an extrusion using this
 | ||||||
|  | @ -57,23 +60,17 @@ float | ||||||
| Flow::spacing(const Flow &other) const { | Flow::spacing(const Flow &other) const { | ||||||
|     assert(this->height == other.height); |     assert(this->height == other.height); | ||||||
|     assert(this->bridge == other.bridge); |     assert(this->bridge == other.bridge); | ||||||
|      |     return this->bridge ?  | ||||||
|     if (this->bridge) { |         0.5f * this->width + 0.5f * other.width + BRIDGE_EXTRA_SPACING : | ||||||
|         return this->width/2 + other.width/2 + BRIDGE_EXTRA_SPACING; |         0.5f * this->spacing() + 0.5f * other.spacing(); | ||||||
|     } |  | ||||||
|      |  | ||||||
|     return this->spacing()/2 + other.spacing()/2; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* This method returns extrusion volume per head move unit. */ | /* This method returns extrusion volume per head move unit. */ | ||||||
| double | double Flow::mm3_per_mm() const  | ||||||
| Flow::mm3_per_mm() const { | { | ||||||
|     if (this->bridge) { |     return this->bridge ? | ||||||
|         return (this->width * this->width) * PI/4.0; |         (this->width * this->width) * PI/4.0 : | ||||||
|     } |         this->width * this->height + (this->height * this->height) / 4.0 * (PI-4.0); | ||||||
|      |  | ||||||
|     // rectangle with semicircles at the ends
 |  | ||||||
|     return this->width * this->height + (this->height*this->height) / 4.0 * (PI-4.0); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* This static method returns bridge width for a given nozzle diameter. */ | /* 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. */ | /* This static method returns a sane extrusion width default. */ | ||||||
| float | float Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) { | ||||||
| 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
 |     // 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
 |     // shape: rectangle with semicircles at the ends
 | ||||||
|     float width = ((nozzle_diameter*nozzle_diameter) * PI + (height*height) * (4.0 - PI)) / (4.0 * height); |     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. */ | /* This static method returns the extrusion width value corresponding to the supplied centerline spacing. */ | ||||||
| float | float Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge)  | ||||||
| Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) { | { | ||||||
|     if (bridge) { |     return bridge ?  | ||||||
|         return spacing - BRIDGE_EXTRA_SPACING; |         (spacing - BRIDGE_EXTRA_SPACING) :  | ||||||
|     } | #ifdef HAS_PERIMETER_LINE_OVERLAP | ||||||
|      |         (spacing + PERIMETER_LINE_OVERLAP_FACTOR * height * (1 - PI/4.0)); | ||||||
|     // rectangle with semicircles at the ends
 | #else | ||||||
|     return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0); |         (spacing + height * (1 - PI/4.0)); | ||||||
|  | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -7,8 +7,14 @@ | ||||||
| 
 | 
 | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| 
 | 
 | ||||||
|  | // Extra spacing of bridge threads, in mm.
 | ||||||
| #define BRIDGE_EXTRA_SPACING 0.05 | #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 { | enum FlowRole { | ||||||
|     frExternalPerimeter, |     frExternalPerimeter, | ||||||
|  | @ -22,9 +28,17 @@ enum FlowRole { | ||||||
| 
 | 
 | ||||||
| class Flow | class Flow | ||||||
| { | { | ||||||
|     public: | public: | ||||||
|     float width, height, nozzle_diameter; |     // Non bridging flow: Maximum width of an extrusion with semicircles at the ends.
 | ||||||
|     bool bridge; |     // 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) |     Flow(float _w, float _h, float _nd, bool _bridge = false) | ||||||
|         : width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}; |         : width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}; | ||||||
|  |  | ||||||
|  | @ -315,8 +315,7 @@ GCode::change_layer(const Layer &layer) | ||||||
|      |      | ||||||
|     // avoid computing islands and overhangs if they're not needed
 |     // avoid computing islands and overhangs if they're not needed
 | ||||||
|     if (this->config.avoid_crossing_perimeters) { |     if (this->config.avoid_crossing_perimeters) { | ||||||
|         ExPolygons islands; |         ExPolygons islands = union_ex(layer.slices, true); | ||||||
|         union_(layer.slices, &islands, true); |  | ||||||
|         this->avoid_crossing_perimeters.init_layer_mp(islands); |         this->avoid_crossing_perimeters.init_layer_mp(islands); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |  | ||||||
|  | @ -105,7 +105,7 @@ Layer::make_slices() | ||||||
|         FOREACH_LAYERREGION(this, layerm) { |         FOREACH_LAYERREGION(this, layerm) { | ||||||
|             polygons_append(slices_p, to_polygons((*layerm)->slices)); |             polygons_append(slices_p, to_polygons((*layerm)->slices)); | ||||||
|         } |         } | ||||||
|         union_(slices_p, &slices); |         slices = union_ex(slices_p); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     this->slices.expolygons.clear(); |     this->slices.expolygons.clear(); | ||||||
|  | @ -132,15 +132,11 @@ Layer::merge_slices() | ||||||
|     if (this->regions.size() == 1) { |     if (this->regions.size() == 1) { | ||||||
|         // Optimization, also more robust. Don't merge classified pieces of layerm->slices,
 |         // 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.
 |         // but use the non-split islands of a layer. For a single region print, these shall be equal.
 | ||||||
|         this->regions.front()->slices.surfaces.clear(); |         this->regions.front()->slices.set(this->slices.expolygons, stInternal); | ||||||
|         surfaces_append(this->regions.front()->slices.surfaces, this->slices.expolygons, stInternal); |  | ||||||
|     } else { |     } else { | ||||||
|         FOREACH_LAYERREGION(this, layerm) { |         FOREACH_LAYERREGION(this, layerm) { | ||||||
|             ExPolygons expp; |  | ||||||
|             // without safety offset, artifacts are generated (GH #2494)
 |             // without safety offset, artifacts are generated (GH #2494)
 | ||||||
|             union_(to_polygons(STDMOVE((*layerm)->slices.surfaces)), &expp, true); |             (*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal); | ||||||
|             (*layerm)->slices.surfaces.clear(); |  | ||||||
|             surfaces_append((*layerm)->slices.surfaces, expp, stInternal); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -223,7 +219,7 @@ Layer::make_perimeters() | ||||||
|                 } |                 } | ||||||
|                 // merge the surfaces assigned to each group
 |                 // merge the surfaces assigned to each group
 | ||||||
|                 for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it) |                 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
 |             // make perimeters
 | ||||||
|  | @ -236,8 +232,7 @@ Layer::make_perimeters() | ||||||
|                     // Separate the fill surfaces.
 |                     // Separate the fill surfaces.
 | ||||||
|                     ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices); |                     ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices); | ||||||
|                     (*l)->fill_expolygons = expp; |                     (*l)->fill_expolygons = expp; | ||||||
|                     (*l)->fill_surfaces.surfaces.clear(); |                     (*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front()); | ||||||
|                     surfaces_append((*l)->fill_surfaces.surfaces, 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 { | 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 Layer; | ||||||
| class PrintRegion; | class PrintRegion; | ||||||
| class PrintObject; | class PrintObject; | ||||||
|  | @ -155,7 +152,7 @@ public: | ||||||
| protected: | protected: | ||||||
|     SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, |     SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, | ||||||
|         coordf_t slice_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); |     Polygons fill_boundaries = to_polygons(this->fill_expolygons); | ||||||
|     this->fill_surfaces.surfaces.clear(); |     this->fill_surfaces.surfaces.clear(); | ||||||
|     for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++ surface) |     for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++ surface) | ||||||
|         surfaces_append( |         this->fill_surfaces.append( | ||||||
|             this->fill_surfaces.surfaces, |  | ||||||
|             intersection_ex(to_polygons(surface->expolygon), fill_boundaries), |             intersection_ex(to_polygons(surface->expolygon), fill_boundaries), | ||||||
|             surface->surface_type); |             surface->surface_type); | ||||||
| } | } | ||||||
|  | @ -91,9 +90,9 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* | ||||||
|     g.process(); |     g.process(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 3.
 | //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
 | ||||||
| //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 1.5
 | //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
 | ||||||
| #define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare, 0. | #define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
| LayerRegion::process_external_surfaces(const Layer* lower_layer) | LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||||
|  | @ -194,7 +193,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                 // Grown by 3mm.
 |                 // 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) { |                 if (idx_island == -1) { | ||||||
|                     printf("Bridge did not fall into the source region!\r\n"); |                     printf("Bridge did not fall into the source region!\r\n"); | ||||||
|                 } else { |                 } else { | ||||||
|  | @ -262,9 +261,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) | ||||||
|                 BridgeDetector bd( |                 BridgeDetector bd( | ||||||
|                     initial, |                     initial, | ||||||
|                     lower_layer->slices, |                     lower_layer->slices, | ||||||
|                     //FIXME parameters are not correct!
 |                     this->flow(frInfill, true).scaled_width() | ||||||
|                     // flow(FlowRole role, bool bridge = false, double width = -1) const;
 |  | ||||||
|                     this->flow(frInfill, true, this->layer()->height).scaled_width() |  | ||||||
|                 ); |                 ); | ||||||
|                 #ifdef SLIC3R_DEBUG |                 #ifdef SLIC3R_DEBUG | ||||||
|                 printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id()); |                 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)); |             polygons_append(polys, STDMOVE(s1)); | ||||||
|             for (size_t j = i + 1; j < top.size(); ++ j) { |             for (size_t j = i + 1; j < top.size(); ++ j) { | ||||||
|                 Surface &s2 = top[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)); |                     polygons_append(polys, STDMOVE(s2)); | ||||||
|  |                     s2.clear(); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             if (s1.surface_type == stTop) |             if (s1.surface_type == stTop) | ||||||
|                 // Trim the top surfaces by the bottom surfaces. This gives the priority to the bottom surfaces.
 |                 // 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)); |         polygons_append(polys, STDMOVE(s1)); | ||||||
|         for (size_t j = i + 1; j < internal.size(); ++ j) { |         for (size_t j = i + 1; j < internal.size(); ++ j) { | ||||||
|             Surface &s2 = internal[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)); |                 polygons_append(polys, STDMOVE(s2)); | ||||||
|  |                 s2.clear(); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         ExPolygons new_expolys = diff_ex(polys, new_polygons); |         ExPolygons new_expolys = diff_ex(polys, new_polygons); | ||||||
|         polygons_append(new_polygons, to_polygons(new_expolys)); |         polygons_append(new_polygons, to_polygons(new_expolys)); | ||||||
|  |  | ||||||
|  | @ -76,20 +76,20 @@ class Linef3 | ||||||
|     void scale(double factor); |     void scale(double factor); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } // namespace Slic3r
 | ||||||
| 
 | 
 | ||||||
| // start Boost
 | // start Boost
 | ||||||
| #include <boost/polygon/polygon.hpp> | #include <boost/polygon/polygon.hpp> | ||||||
| namespace boost { namespace polygon { | namespace boost { namespace polygon { | ||||||
|     template <> |     template <> | ||||||
|     struct geometry_concept<Line> { typedef segment_concept type; }; |     struct geometry_concept<Slic3r::Line> { typedef segment_concept type; }; | ||||||
| 
 | 
 | ||||||
|     template <> |     template <> | ||||||
|     struct segment_traits<Line> { |     struct segment_traits<Slic3r::Line> { | ||||||
|         typedef coord_t coordinate_type; |         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; |             return dir.to_int() ? line.b : line.a; | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| #include "Layer.hpp" | #include "Layer.hpp" | ||||||
| #include "Point.hpp" | #include "Point.hpp" | ||||||
| #include "TriangleMesh.hpp" | #include "TriangleMesh.hpp" | ||||||
|  | #include "Slicing.hpp" | ||||||
| #include <map> | #include <map> | ||||||
| #include <string> | #include <string> | ||||||
| #include <utility> | #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()
 |         // grow our environment slightly in order for simplify_by_visibility()
 | ||||||
|         // to work best by considering moves on boundaries valid as well
 |         // 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 (island_idx == -1) { | ||||||
|             /*  If 'from' or 'to' are not inside our env, they were connected using the 
 |             /*  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)) { |             if (!grown_env.contains(from)) { | ||||||
|                 // delete second point while the line connecting first to third crosses the
 |                 // delete second point while the line connecting first to third crosses the
 | ||||||
|                 // boundaries as many times as the current first to second
 |                 // 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); |                     polyline.points.erase(polyline.points.begin() + 1); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             if (!grown_env.contains(to)) { |             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); |                     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); |         size_t result = from.nearest_waypoint_index(pp, to); | ||||||
|          |          | ||||||
|         // as we assume 'from' is outside env, any node will require at least one crossing
 |         // 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
 |             // discard result
 | ||||||
|             pp.erase(pp.begin() + result); |             pp.erase(pp.begin() + result); | ||||||
|         } else { |         } else { | ||||||
|  |  | ||||||
|  | @ -54,8 +54,7 @@ PerimeterGenerator::process() | ||||||
|     for (Surfaces::const_iterator surface = this->slices->surfaces.begin(); |     for (Surfaces::const_iterator surface = this->slices->surfaces.begin(); | ||||||
|         surface != this->slices->surfaces.end(); ++surface) { |         surface != this->slices->surfaces.end(); ++surface) { | ||||||
|         // detect how many perimeters must be generated for this island
 |         // detect how many perimeters must be generated for this island
 | ||||||
|         signed short loop_number = this->config->perimeters + surface->extra_perimeters; |         const int loop_number = this->config->perimeters + surface->extra_perimeters -1;  // 0-indexed loops
 | ||||||
|         loop_number--;  // 0-indexed loops
 |  | ||||||
|          |          | ||||||
|         Polygons gaps; |         Polygons gaps; | ||||||
|          |          | ||||||
|  | @ -67,7 +66,7 @@ PerimeterGenerator::process() | ||||||
|             ThickPolylines thin_walls; |             ThickPolylines thin_walls; | ||||||
|              |              | ||||||
|             // we loop one time more than needed in order to find gaps after the last perimeter was applied
 |             // 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; |                 Polygons offsets; | ||||||
|                 if (i == 0) { |                 if (i == 0) { | ||||||
|                     // the minimum thickness of a single loop is:
 |                     // the minimum thickness of a single loop is:
 | ||||||
|  | @ -170,16 +169,16 @@ PerimeterGenerator::process() | ||||||
|             } |             } | ||||||
|              |              | ||||||
|             // nest loops: holes first
 |             // 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]; |                 PerimeterGeneratorLoops &holes_d = holes[d]; | ||||||
|                  |                  | ||||||
|                 // loop through all holes having depth == 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]; |                     const PerimeterGeneratorLoop &loop = holes_d[i]; | ||||||
|                      |                      | ||||||
|                     // find the hole loop that contains this one, if any
 |                     // find the hole loop that contains this one, if any
 | ||||||
|                     for (signed short t = d+1; t <= loop_number; ++t) { |                     for (int t = d+1; t <= loop_number; ++t) { | ||||||
|                         for (signed short j = 0; j < holes[t].size(); ++j) { |                         for (int j = 0; j < (int)holes[t].size(); ++j) { | ||||||
|                             PerimeterGeneratorLoop &candidate_parent = holes[t][j]; |                             PerimeterGeneratorLoop &candidate_parent = holes[t][j]; | ||||||
|                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) { |                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) { | ||||||
|                                 candidate_parent.children.push_back(loop); |                                 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
 |                     // if no hole contains this hole, find the contour loop that contains it
 | ||||||
|                     for (signed short t = loop_number; t >= 0; --t) { |                     for (int t = loop_number; t >= 0; --t) { | ||||||
|                         for (signed short j = 0; j < contours[t].size(); ++j) { |                         for (int j = 0; j < (int)contours[t].size(); ++j) { | ||||||
|                             PerimeterGeneratorLoop &candidate_parent = contours[t][j]; |                             PerimeterGeneratorLoop &candidate_parent = contours[t][j]; | ||||||
|                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) { |                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) { | ||||||
|                                 candidate_parent.children.push_back(loop); |                                 candidate_parent.children.push_back(loop); | ||||||
|  | @ -207,16 +206,16 @@ PerimeterGenerator::process() | ||||||
|             } |             } | ||||||
|          |          | ||||||
|             // nest contour loops
 |             // 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]; |                 PerimeterGeneratorLoops &contours_d = contours[d]; | ||||||
|                  |                  | ||||||
|                 // loop through all contours having depth == 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]; |                     const PerimeterGeneratorLoop &loop = contours_d[i]; | ||||||
|                  |                  | ||||||
|                     // find the contour loop that contains it
 |                     // find the contour loop that contains it
 | ||||||
|                     for (signed short t = d-1; t >= 0; --t) { |                     for (int t = d-1; t >= 0; --t) { | ||||||
|                         for (signed short j = 0; j < contours[t].size(); ++j) { |                         for (int j = 0; j < contours[t].size(); ++j) { | ||||||
|                             PerimeterGeneratorLoop &candidate_parent = contours[t][j]; |                             PerimeterGeneratorLoop &candidate_parent = contours[t][j]; | ||||||
|                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) { |                             if (candidate_parent.polygon.contains(loop.polygon.first_point())) { | ||||||
|                                 candidate_parent.children.push_back(loop); |                                 candidate_parent.children.push_back(loop); | ||||||
|  | @ -315,8 +314,7 @@ PerimeterGenerator::process() | ||||||
|             coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE); |             coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE); | ||||||
|              |              | ||||||
|             // append infill areas to fill_surfaces
 |             // append infill areas to fill_surfaces
 | ||||||
|             surfaces_append( |             this->fill_surfaces->append( | ||||||
|                 this->fill_surfaces->surfaces,  |  | ||||||
|                 offset2_ex( |                 offset2_ex( | ||||||
|                     pp, |                     pp, | ||||||
|                     -inset -min_perimeter_infill_spacing/2, |                     -inset -min_perimeter_infill_spacing/2, | ||||||
|  | @ -354,36 +352,24 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, | ||||||
|         if (this->config->overhangs && this->layer_id > 0 |         if (this->config->overhangs && this->layer_id > 0 | ||||||
|             && !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 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
 |             // get non-overhang paths by intersecting this loop with the grown lower slices
 | ||||||
|             { |             extrusion_paths_append( | ||||||
|                 Polylines polylines; |                 paths, | ||||||
|                 intersection((Polygons)loop->polygon, this->_lower_slices_p, &polylines); |                 intersection_pl(loop->polygon, this->_lower_slices_p), | ||||||
|                  |                 role, | ||||||
|                 for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) { |                 is_external ? this->_ext_mm3_per_mm           : this->_mm3_per_mm, | ||||||
|                     ExtrusionPath path(role); |                 is_external ? this->ext_perimeter_flow.width  : this->perimeter_flow.width, | ||||||
|                     path.polyline   = *polyline; |                 this->layer_height); | ||||||
|                     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); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|              |              | ||||||
|             // get overhang paths by checking what parts of this loop fall 
 |             // get overhang paths by checking what parts of this loop fall 
 | ||||||
|             // outside the grown lower slices (thus where the distance between
 |             // outside the grown lower slices (thus where the distance between
 | ||||||
|             // the loop centerline and original lower slices is >= half nozzle diameter
 |             // the loop centerline and original lower slices is >= half nozzle diameter
 | ||||||
|             { |             extrusion_paths_append( | ||||||
|                 Polylines polylines; |                 paths, | ||||||
|                 diff((Polygons)loop->polygon, this->_lower_slices_p, &polylines); |                 diff_pl(loop->polygon, this->_lower_slices_p), | ||||||
|                  |                 erOverhangPerimeter, | ||||||
|                 for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) { |                 this->_mm3_per_mm_overhang, | ||||||
|                     ExtrusionPath path(erOverhangPerimeter); |                 this->overhang_flow.width, | ||||||
|                     path.polyline   = *polyline; |                 this->overhang_flow.height); | ||||||
|                     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); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|              |              | ||||||
|             // reapply the nearest point search for starting point
 |             // reapply the nearest point search for starting point
 | ||||||
|             // We allow polyline reversal because Clipper may have randomly
 |             // We allow polyline reversal because Clipper may have randomly
 | ||||||
|  | @ -459,7 +445,7 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo | ||||||
|         ExtrusionPath path(role); |         ExtrusionPath path(role); | ||||||
|         ThickLines lines = p->thicklines(); |         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 ThickLine& line = lines[i]; | ||||||
|              |              | ||||||
|             const coordf_t line_len = line.length(); |             const coordf_t line_len = line.length(); | ||||||
|  |  | ||||||
|  | @ -12,12 +12,6 @@ Point::Point(double x, double y) | ||||||
|     this->y = lrint(y); |     this->y = lrint(y); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool |  | ||||||
| Point::operator==(const Point& rhs) const |  | ||||||
| { |  | ||||||
|     return this->coincides_with(rhs); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| std::string | std::string | ||||||
| Point::wkt() const | Point::wkt() const | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -36,7 +36,7 @@ class Point | ||||||
|     static Point new_scale(coordf_t x, coordf_t y) { |     static Point new_scale(coordf_t x, coordf_t y) { | ||||||
|         return Point(scale_(x), scale_(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 wkt() const; | ||||||
|     std::string dump_perl() const; |     std::string dump_perl() const; | ||||||
|     void scale(double factor); |     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-(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); } | 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 | class Point3 : public Point | ||||||
| { | { | ||||||
|     public: |     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-(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*(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 | class Pointf3 : public Pointf | ||||||
| { | { | ||||||
|  | @ -122,7 +131,7 @@ class Pointf3 : public Pointf | ||||||
|     Vectorf3 vector_to(const Pointf3 &point) const; |     Vectorf3 vector_to(const Pointf3 &point) const; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } // namespace Slic3r
 | ||||||
| 
 | 
 | ||||||
| // start Boost
 | // start Boost
 | ||||||
| #include <boost/version.hpp> | #include <boost/version.hpp> | ||||||
|  | @ -146,28 +155,28 @@ namespace boost { namespace polygon { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     template <> |     template <> | ||||||
|     struct geometry_concept<Point> { typedef point_concept type; }; |     struct geometry_concept<Slic3r::Point> { typedef point_concept type; }; | ||||||
|     |     | ||||||
|     template <> |     template <> | ||||||
|     struct point_traits<Point> { |     struct point_traits<Slic3r::Point> { | ||||||
|         typedef coord_t coordinate_type; |         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; |             return (orient == HORIZONTAL) ? point.x : point.y; | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|      |      | ||||||
|     template <> |     template <> | ||||||
|     struct point_mutable_traits<Point> { |     struct point_mutable_traits<Slic3r::Point> { | ||||||
|         typedef coord_t coordinate_type; |         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) |             if (orient == HORIZONTAL) | ||||||
|                 point.x = value; |                 point.x = value; | ||||||
|             else |             else | ||||||
|                 point.y = value; |                 point.y = value; | ||||||
|         } |         } | ||||||
|         static inline Point construct(coord_t x_value, coord_t y_value) { |         static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) { | ||||||
|             Point retval; |             Slic3r::Point retval; | ||||||
|             retval.x = x_value; |             retval.x = x_value; | ||||||
|             retval.y = y_value;  |             retval.y = y_value;  | ||||||
|             return retval; |             return retval; | ||||||
|  |  | ||||||
|  | @ -112,9 +112,7 @@ double Polygon::area() const | ||||||
| bool | bool | ||||||
| Polygon::is_counter_clockwise() const | Polygon::is_counter_clockwise() const | ||||||
| { | { | ||||||
|     ClipperLib::Path p; |     return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this)); | ||||||
|     Slic3rMultiPoint_to_ClipperPath(*this, &p); |  | ||||||
|     return ClipperLib::Orientation(p); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool | bool | ||||||
|  | @ -190,8 +188,7 @@ Polygon::simplify(double tolerance) const | ||||||
|      |      | ||||||
|     Polygons pp; |     Polygons pp; | ||||||
|     pp.push_back(p); |     pp.push_back(p); | ||||||
|     simplify_polygons(pp, &pp); |     return simplify_polygons(pp); | ||||||
|     return pp; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
|  |  | ||||||
|  | @ -142,43 +142,43 @@ inline Polylines to_polylines(Polygons &&polys) | ||||||
| #include <boost/polygon/polygon.hpp> | #include <boost/polygon/polygon.hpp> | ||||||
| namespace boost { namespace polygon { | namespace boost { namespace polygon { | ||||||
|     template <> |     template <> | ||||||
|     struct geometry_concept<Polygon>{ typedef polygon_concept type; }; |     struct geometry_concept<Slic3r::Polygon>{ typedef polygon_concept type; }; | ||||||
| 
 | 
 | ||||||
|     template <> |     template <> | ||||||
|     struct polygon_traits<Polygon> { |     struct polygon_traits<Slic3r::Polygon> { | ||||||
|         typedef coord_t coordinate_type; |         typedef coord_t coordinate_type; | ||||||
|         typedef Points::const_iterator iterator_type; |         typedef Slic3r::Points::const_iterator iterator_type; | ||||||
|         typedef Point point_type; |         typedef Slic3r::Point point_type; | ||||||
| 
 | 
 | ||||||
|         // Get the begin iterator
 |         // 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(); |             return t.points.begin(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Get the end iterator
 |         // 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(); |             return t.points.end(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Get the number of sides of the polygon
 |         // 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(); |             return t.points.size(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Get the winding direction of the polygon
 |         // 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; |             return unknown_winding; | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     template <> |     template <> | ||||||
|     struct polygon_mutable_traits<Polygon> { |     struct polygon_mutable_traits<Slic3r::Polygon> { | ||||||
|         // expects stl style iterators
 |         // expects stl style iterators
 | ||||||
|         template <typename iT> |         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(); |             polygon.points.clear(); | ||||||
|             while (input_begin != input_end) { |             while (input_begin != input_end) { | ||||||
|                 polygon.points.push_back(Point()); |                 polygon.points.push_back(Slic3r::Point()); | ||||||
|                 boost::polygon::assign(polygon.points.back(), *input_begin); |                 boost::polygon::assign(polygon.points.back(), *input_begin); | ||||||
|                 ++input_begin; |                 ++input_begin; | ||||||
|             } |             } | ||||||
|  | @ -189,32 +189,32 @@ namespace boost { namespace polygon { | ||||||
|     }; |     }; | ||||||
|      |      | ||||||
|     template <> |     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
 |     //next we map to the concept through traits
 | ||||||
|     template <> |     template <> | ||||||
|     struct polygon_set_traits<Polygons> { |     struct polygon_set_traits<Slic3r::Polygons> { | ||||||
|         typedef coord_t coordinate_type; |         typedef coord_t coordinate_type; | ||||||
|         typedef Polygons::const_iterator iterator_type; |         typedef Slic3r::Polygons::const_iterator iterator_type; | ||||||
|         typedef Polygons operator_arg_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(); |             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(); |             return polygon_set.end(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         //don't worry about these, just return false from them
 |         //don't worry about these, just return false from them
 | ||||||
|         static inline bool clean(const Polygons& polygon_set) { return false; } |         static inline bool clean(const Slic3r::Polygons& polygon_set) { return false; } | ||||||
|         static inline bool sorted(const Polygons& polygon_set) { return false; } |         static inline bool sorted(const Slic3r::Polygons& polygon_set) { return false; } | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     template <> |     template <> | ||||||
|     struct polygon_set_mutable_traits<Polygons> { |     struct polygon_set_mutable_traits<Slic3r::Polygons> { | ||||||
|         template <typename input_iterator_type> |         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); |           polygons.assign(input_begin, input_end); | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  | @ -133,12 +133,6 @@ Print::clear_regions() | ||||||
|         this->delete_region(i); |         this->delete_region(i); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| PrintRegion* |  | ||||||
| Print::get_region(size_t idx) |  | ||||||
| { |  | ||||||
|     return regions.at(idx); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| PrintRegion* | PrintRegion* | ||||||
| Print::add_region() | Print::add_region() | ||||||
| { | { | ||||||
|  | @ -608,20 +602,15 @@ Print::validate() const | ||||||
|                 object->model_object()->instances.front()->transform_polygon(&convex_hull); |                 object->model_object()->instances.front()->transform_polygon(&convex_hull); | ||||||
|                  |                  | ||||||
|                 // grow convex hull with the clearance margin
 |                 // grow convex hull with the clearance margin
 | ||||||
|                 { |                 convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front(); | ||||||
|                     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(); |  | ||||||
|                 } |  | ||||||
|                  |                  | ||||||
|                 // now we check that no instance of convex_hull intersects any of the previously checked object instances
 |                 // 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) { |                 for (Points::const_iterator copy = object->_shifted_copies.begin(); copy != object->_shifted_copies.end(); ++copy) { | ||||||
|                     Polygon p = convex_hull; |                     Polygon p = convex_hull; | ||||||
|                     p.translate(*copy); |                     p.translate(*copy); | ||||||
|                     if (intersects(a, p)) |                     if (! intersection(a, p).empty()) | ||||||
|                         return "Some objects are too close; your extruder will collide with them."; |                         return "Some objects are too close; your extruder will collide with them."; | ||||||
|                      |                     polygons_append(a, p); | ||||||
|                     union_(a, p, &a); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ | ||||||
| #include "Layer.hpp" | #include "Layer.hpp" | ||||||
| #include "Model.hpp" | #include "Model.hpp" | ||||||
| #include "PlaceholderParser.hpp" | #include "PlaceholderParser.hpp" | ||||||
| 
 | #include "Slicing.hpp" | ||||||
| 
 | 
 | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| 
 | 
 | ||||||
|  | @ -78,6 +78,10 @@ public: | ||||||
|     std::map< size_t,std::vector<int> > region_volumes; |     std::map< size_t,std::vector<int> > region_volumes; | ||||||
|     PrintObjectConfig config; |     PrintObjectConfig config; | ||||||
|     t_layer_height_ranges layer_height_ranges; |     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
 |     // 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
 |     // so that next call to make_perimeters() performs a union() before computing loops
 | ||||||
|  | @ -136,13 +140,27 @@ public: | ||||||
|     bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys); |     bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys); | ||||||
|     bool invalidate_step(PrintObjectStep step); |     bool invalidate_step(PrintObjectStep step); | ||||||
|     bool invalidate_all_steps(); |     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; |     bool has_support_material() const; | ||||||
|     void detect_surfaces_type(); |     void detect_surfaces_type(); | ||||||
|     void process_external_surfaces(); |     void process_external_surfaces(); | ||||||
|     void discover_vertical_shells(); |     void discover_vertical_shells(); | ||||||
|     void bridge_over_infill(); |     void bridge_over_infill(); | ||||||
|      |     void _make_perimeters(); | ||||||
|  |     void _infill(); | ||||||
|  |     void _generate_support_material(); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     Print* _print; |     Print* _print; | ||||||
|     ModelObject* _model_object; |     ModelObject* _model_object; | ||||||
|  | @ -152,6 +170,8 @@ private: | ||||||
|         // parameter
 |         // parameter
 | ||||||
|     PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); |     PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); | ||||||
|     ~PrintObject() {} |     ~PrintObject() {} | ||||||
|  | 
 | ||||||
|  |     std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| typedef std::vector<PrintObject*> PrintObjectPtrs; | typedef std::vector<PrintObject*> PrintObjectPtrs; | ||||||
|  | @ -186,7 +206,8 @@ class Print | ||||||
|     bool reload_model_instances(); |     bool reload_model_instances(); | ||||||
| 
 | 
 | ||||||
|     // methods for handling regions
 |     // 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(); |     PrintRegion* add_region(); | ||||||
|      |      | ||||||
|     // methods for handling state
 |     // methods for handling state
 | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| #include "PrintConfig.hpp" | #include "PrintConfig.hpp" | ||||||
|  | #include <boost/thread.hpp> | ||||||
| 
 | 
 | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| 
 | 
 | ||||||
|  | @ -1120,6 +1121,17 @@ PrintConfigDef::PrintConfigDef() | ||||||
|     def->cli = "support-material!"; |     def->cli = "support-material!"; | ||||||
|     def->default_value = new ConfigOptionBool(false); |     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 = this->add("support_material_angle", coInt); | ||||||
|     def->label = "Pattern angle"; |     def->label = "Pattern angle"; | ||||||
|     def->category = "Support material"; |     def->category = "Support material"; | ||||||
|  | @ -1177,6 +1189,13 @@ PrintConfigDef::PrintConfigDef() | ||||||
|     def->cli = "support-material-extrusion-width=s"; |     def->cli = "support-material-extrusion-width=s"; | ||||||
|     def->default_value = new ConfigOptionFloatOrPercent(0, false); |     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 = this->add("support_material_interface_extruder", coInt); | ||||||
|     def->label = "Support material/raft interface extruder"; |     def->label = "Support material/raft interface extruder"; | ||||||
|     def->category = "Extruders"; |     def->category = "Extruders"; | ||||||
|  | @ -1247,6 +1266,13 @@ PrintConfigDef::PrintConfigDef() | ||||||
|     def->min = 0; |     def->min = 0; | ||||||
|     def->default_value = new ConfigOptionFloat(60); |     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 = this->add("support_material_threshold", coInt); | ||||||
|     def->label = "Overhang threshold"; |     def->label = "Overhang threshold"; | ||||||
|     def->category = "Support material"; |     def->category = "Support material"; | ||||||
|  | @ -1290,9 +1316,11 @@ PrintConfigDef::PrintConfigDef() | ||||||
|     def->cli = "threads|j=i"; |     def->cli = "threads|j=i"; | ||||||
|     def->readonly = true; |     def->readonly = true; | ||||||
|     def->min = 1; |     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 = this->add("toolchange_gcode", coString); | ||||||
|     def->label = "Tool change G-code"; |     def->label = "Tool change G-code"; | ||||||
|     def->tooltip = "This custom code is inserted right before every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder]."; |     def->tooltip = "This custom code is inserted right before every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder]."; | ||||||
|  |  | ||||||
|  | @ -153,6 +153,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig | ||||||
|     ConfigOptionInt                 support_material_enforce_layers; |     ConfigOptionInt                 support_material_enforce_layers; | ||||||
|     ConfigOptionInt                 support_material_extruder; |     ConfigOptionInt                 support_material_extruder; | ||||||
|     ConfigOptionFloatOrPercent      support_material_extrusion_width; |     ConfigOptionFloatOrPercent      support_material_extrusion_width; | ||||||
|  |     ConfigOptionBool                support_material_interface_contact_loops; | ||||||
|     ConfigOptionInt                 support_material_interface_extruder; |     ConfigOptionInt                 support_material_interface_extruder; | ||||||
|     ConfigOptionInt                 support_material_interface_layers; |     ConfigOptionInt                 support_material_interface_layers; | ||||||
|     ConfigOptionFloat               support_material_interface_spacing; |     ConfigOptionFloat               support_material_interface_spacing; | ||||||
|  | @ -160,8 +161,10 @@ class PrintObjectConfig : public virtual StaticPrintConfig | ||||||
|     ConfigOptionEnum<SupportMaterialPattern> support_material_pattern; |     ConfigOptionEnum<SupportMaterialPattern> support_material_pattern; | ||||||
|     ConfigOptionFloat               support_material_spacing; |     ConfigOptionFloat               support_material_spacing; | ||||||
|     ConfigOptionFloat               support_material_speed; |     ConfigOptionFloat               support_material_speed; | ||||||
|  |     ConfigOptionBool                support_material_synchronize_layers; | ||||||
|     ConfigOptionInt                 support_material_threshold; |     ConfigOptionInt                 support_material_threshold; | ||||||
|     ConfigOptionBool                support_material_with_sheath; |     ConfigOptionBool                support_material_with_sheath; | ||||||
|  |     ConfigOptionFloatOrPercent      support_material_xy_spacing; | ||||||
|     ConfigOptionFloat               xy_size_compensation; |     ConfigOptionFloat               xy_size_compensation; | ||||||
|      |      | ||||||
|     PrintObjectConfig(bool initialize = true) : StaticPrintConfig() { |     PrintObjectConfig(bool initialize = true) : StaticPrintConfig() { | ||||||
|  | @ -185,6 +188,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig | ||||||
|         OPT_PTR(support_material_buildplate_only); |         OPT_PTR(support_material_buildplate_only); | ||||||
|         OPT_PTR(support_material_contact_distance); |         OPT_PTR(support_material_contact_distance); | ||||||
|         OPT_PTR(support_material_enforce_layers); |         OPT_PTR(support_material_enforce_layers); | ||||||
|  |         OPT_PTR(support_material_interface_contact_loops); | ||||||
|         OPT_PTR(support_material_extruder); |         OPT_PTR(support_material_extruder); | ||||||
|         OPT_PTR(support_material_extrusion_width); |         OPT_PTR(support_material_extrusion_width); | ||||||
|         OPT_PTR(support_material_interface_extruder); |         OPT_PTR(support_material_interface_extruder); | ||||||
|  | @ -194,6 +198,8 @@ class PrintObjectConfig : public virtual StaticPrintConfig | ||||||
|         OPT_PTR(support_material_pattern); |         OPT_PTR(support_material_pattern); | ||||||
|         OPT_PTR(support_material_spacing); |         OPT_PTR(support_material_spacing); | ||||||
|         OPT_PTR(support_material_speed); |         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_threshold); | ||||||
|         OPT_PTR(support_material_with_sheath); |         OPT_PTR(support_material_with_sheath); | ||||||
|         OPT_PTR(xy_size_compensation); |         OPT_PTR(xy_size_compensation); | ||||||
|  |  | ||||||
|  | @ -2,10 +2,28 @@ | ||||||
| #include "BoundingBox.hpp" | #include "BoundingBox.hpp" | ||||||
| #include "ClipperUtils.hpp" | #include "ClipperUtils.hpp" | ||||||
| #include "Geometry.hpp" | #include "Geometry.hpp" | ||||||
| #include "SVG.hpp" | #include "SupportMaterial.hpp" | ||||||
|  | 
 | ||||||
|  | #include <boost/log/trivial.hpp> | ||||||
| 
 | 
 | ||||||
| #include <Shiny/Shiny.h> | #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 { | namespace Slic3r { | ||||||
| 
 | 
 | ||||||
| PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) | PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) | ||||||
|  | @ -115,8 +133,12 @@ PrintObject::layer_count() const | ||||||
| void | void | ||||||
| PrintObject::clear_layers() | PrintObject::clear_layers() | ||||||
| { | { | ||||||
|     for (int i = this->layers.size()-1; i >= 0; --i) |     for (size_t i = 0; i < this->layers.size(); ++ i) { | ||||||
|         this->delete_layer(i); |         Layer *layer = this->layers[i]; | ||||||
|  |         layer->upper_layer = layer->lower_layer = nullptr; | ||||||
|  |         delete layer; | ||||||
|  |     } | ||||||
|  |     this->layers.clear(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Layer* | Layer* | ||||||
|  | @ -144,8 +166,12 @@ PrintObject::support_layer_count() const | ||||||
| void | void | ||||||
| PrintObject::clear_support_layers() | PrintObject::clear_support_layers() | ||||||
| { | { | ||||||
|     for (int i = this->support_layers.size()-1; i >= 0; --i) |     for (size_t i = 0; i < this->support_layers.size(); ++ i) { | ||||||
|         this->delete_support_layer(i); |         Layer *layer = this->support_layers[i]; | ||||||
|  |         layer->upper_layer = layer->lower_layer = nullptr; | ||||||
|  |         delete layer; | ||||||
|  |     } | ||||||
|  |     this->support_layers.clear(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SupportLayer* | 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_extruder" | ||||||
|             || *opt_key == "support_material_extrusion_width" |             || *opt_key == "support_material_extrusion_width" | ||||||
|             || *opt_key == "support_material_interface_layers" |             || *opt_key == "support_material_interface_layers" | ||||||
|  |             || *opt_key == "support_material_interface_contact_loops" | ||||||
|             || *opt_key == "support_material_interface_extruder" |             || *opt_key == "support_material_interface_extruder" | ||||||
|             || *opt_key == "support_material_interface_spacing" |             || *opt_key == "support_material_interface_spacing" | ||||||
|             || *opt_key == "support_material_interface_speed" |             || *opt_key == "support_material_interface_speed" | ||||||
|             || *opt_key == "support_material_buildplate_only" |             || *opt_key == "support_material_buildplate_only" | ||||||
|             || *opt_key == "support_material_pattern" |             || *opt_key == "support_material_pattern" | ||||||
|  |             || *opt_key == "support_material_xy_spacing" | ||||||
|             || *opt_key == "support_material_spacing" |             || *opt_key == "support_material_spacing" | ||||||
|  |             || *opt_key == "support_material_synchronize_layers" | ||||||
|             || *opt_key == "support_material_threshold" |             || *opt_key == "support_material_threshold" | ||||||
|             || *opt_key == "support_material_with_sheath" |             || *opt_key == "support_material_with_sheath" | ||||||
|             || *opt_key == "dont_support_bridges" |             || *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.
 | // 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() | 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) { |     for (int idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|         for (int idx_layer = 0; idx_layer < int(this->layer_count()); ++ idx_layer) { |         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 topbottom = to_polygons(top); | ||||||
|                 polygons_append(topbottom, to_polygons(bottom)); |                 polygons_append(topbottom, to_polygons(bottom)); | ||||||
|                 surfaces_append(layerm->slices.surfaces,  |                 layerm->slices.append( | ||||||
| #if 0 | #if 0 | ||||||
|                     offset2_ex(diff(layerm_slices_surfaces, topbottom, true), -offset, offset), |                     offset2_ex(diff(layerm_slices_surfaces, topbottom, true), -offset, offset), | ||||||
| #else | #else | ||||||
|  | @ -444,8 +474,8 @@ void PrintObject::detect_surfaces_type() | ||||||
|                     stInternal); |                     stInternal); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             surfaces_append(layerm->slices.surfaces, STDMOVE(top)); |             layerm->slices.append(STDMOVE(top)); | ||||||
|             surfaces_append(layerm->slices.surfaces, STDMOVE(bottom)); |             layerm->slices.append(STDMOVE(bottom)); | ||||||
|              |              | ||||||
| //            Slic3r::debugf "  layer %d has %d bottom, %d top and %d internal surfaces\n",
 | //            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;
 | //                $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
 | ||||||
|  | @ -469,6 +499,8 @@ void PrintObject::detect_surfaces_type() | ||||||
| void | void | ||||||
| PrintObject::process_external_surfaces() | PrintObject::process_external_surfaces() | ||||||
| { | { | ||||||
|  |     BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..."; | ||||||
|  | 
 | ||||||
|     FOREACH_REGION(this->_print, region) { |     FOREACH_REGION(this->_print, region) { | ||||||
|         size_t region_id = region - this->_print->regions.begin(); |         size_t region_id = region - this->_print->regions.begin(); | ||||||
|          |          | ||||||
|  | @ -497,6 +529,8 @@ PrintObject::discover_vertical_shells() | ||||||
| { | { | ||||||
|     PROFILE_FUNC(); |     PROFILE_FUNC(); | ||||||
| 
 | 
 | ||||||
|  |     BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..."; | ||||||
|  | 
 | ||||||
|     const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; |     const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; | ||||||
| 
 | 
 | ||||||
|     for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) { |     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); |             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]; |             Layer       *layer               = this->layers[idx_layer]; | ||||||
|             LayerRegion *layerm              = layer->get_region(idx_region); |             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); |             Flow         solid_infill_flow   = layerm->flow(frSolidInfill); | ||||||
|             coord_t      infill_line_spacing = solid_infill_flow.scaled_spacing();  |             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.
 |             // 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) |             if (1) | ||||||
|             { |             { | ||||||
|                 PROFILE_BLOCK(discover_vertical_shells_region_layer_collect); |                 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; | 					Slic3r::SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", debug_idx), this->bounding_box()); | ||||||
|                     SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", idx), this->bounding_box()); |  | ||||||
|                     for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) { |                     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()) |                         if (n < 0 || n >= (int)this->layers.size()) | ||||||
|                             continue; |                             continue; | ||||||
|                         ExPolygons &expolys = this->layers[n]->perimeter_expolygons; |                         ExPolygons &expolys = this->layers[n]->perimeter_expolygons; | ||||||
|                         for (size_t i = 0; i < expolys.size(); ++ i) { |                         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(expolys[i]); | ||||||
|                             svg.draw_outline(expolys[i].contour, "black", scale_(0.05)); |                             svg.draw_outline(expolys[i].contour, "black", scale_(0.05)); | ||||||
|                             svg.draw_outline(expolys[i].holes, "blue", 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)); |                             svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05)); | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     ++ idx; |  | ||||||
|                 } |                 } | ||||||
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | ||||||
|                 // Reset the top / bottom inflated regions caches of entries, which are out of the moving window.
 |                 // 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 | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|                 { |                 { | ||||||
|                     static size_t idx = 0; | 					Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); | ||||||
|                     SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", idx ++), get_extents(shell)); |  | ||||||
|                     svg.draw(shell); |                     svg.draw(shell); | ||||||
|                     svg.draw_outline(shell, "black", scale_(0.05)); |                     svg.draw_outline(shell, "black", scale_(0.05)); | ||||||
|                     svg.Close();  |                     svg.Close();  | ||||||
|  | @ -634,8 +677,7 @@ PrintObject::discover_vertical_shells() | ||||||
| 
 | 
 | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|             { |             { | ||||||
|                 static size_t idx = 0; |                 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell)); | ||||||
|                 SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", idx ++), get_extents(shell)); |  | ||||||
|                 svg.draw(shell_ex); |                 svg.draw(shell_ex); | ||||||
|                 svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); |                 svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); | ||||||
|                 svg.Close();   |                 svg.Close();   | ||||||
|  | @ -644,8 +686,7 @@ PrintObject::discover_vertical_shells() | ||||||
| 
 | 
 | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|             { |             { | ||||||
|                 static size_t idx = 0; |                 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", debug_idx), get_extents(shell)); | ||||||
|                 SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", idx ++), get_extents(shell)); |  | ||||||
|                 svg.draw(layerm->fill_surfaces.filter_by_type(stInternal), "yellow", 0.5); |                 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_outline(layerm->fill_surfaces.filter_by_type(stInternal), "black", "blue", scale_(0.05)); | ||||||
|                 svg.draw(shell_ex, "blue", 0.5); |                 svg.draw(shell_ex, "blue", 0.5); | ||||||
|  | @ -653,8 +694,7 @@ PrintObject::discover_vertical_shells() | ||||||
|                 svg.Close(); |                 svg.Close(); | ||||||
|             }  |             }  | ||||||
|             { |             { | ||||||
|                 static size_t idx = 0; |                 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); | ||||||
|                 SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell)); |  | ||||||
|                 svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); |                 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_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); | ||||||
|                 svg.draw(shell_ex, "blue", 0.5); |                 svg.draw(shell_ex, "blue", 0.5); | ||||||
|  | @ -662,8 +702,7 @@ PrintObject::discover_vertical_shells() | ||||||
|                 svg.Close(); |                 svg.Close(); | ||||||
|             }  |             }  | ||||||
|             { |             { | ||||||
|                 static size_t idx = 0; |                 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); | ||||||
|                 SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell)); |  | ||||||
|                 svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); |                 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_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); | ||||||
|                 svg.draw(shell_ex, "blue", 0.5); |                 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.
 |             // Trim the shells region by the internal & internal void surfaces.
 | ||||||
|             const SurfaceType surfaceTypesInternal[] = { stInternal, stInternalVoid, stInternalSolid }; |             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); |             shell = intersection(shell, polygonsInternal, true); | ||||||
|             polygons_append(shell, diff(polygonsInternal, holes)); |             polygons_append(shell, diff(polygonsInternal, holes)); | ||||||
|             if (shell.empty()) |             if (shell.empty()) | ||||||
|  | @ -692,8 +731,7 @@ PrintObject::discover_vertical_shells() | ||||||
| #if 1 | #if 1 | ||||||
|             // Intentionally inflate a bit more than how much the region has been shrunk, 
 |             // 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).
 |             // 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, |             shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare); | ||||||
|                 CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare); |  | ||||||
|             if (shell.empty()) |             if (shell.empty()) | ||||||
|                 continue; |                 continue; | ||||||
| #else | #else | ||||||
|  | @ -705,7 +743,7 @@ PrintObject::discover_vertical_shells() | ||||||
|             // get a triangle in $too_narrow; if we grow it below then the shell
 |             // 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
 |             // 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.
 |             // 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()) { |             if (! too_narrow.empty()) { | ||||||
|                 // grow the collapsing parts and add the extra area to  the neighbor layer 
 |                 // 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 
 |                 // 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); |             ExPolygons new_internal_solid = intersection_ex(polygonsInternal, shell, false); | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|             { |             { | ||||||
|                 static size_t idx = 0; |                 Slic3r::SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", debug_idx), get_extents(shell_before)); | ||||||
|                 SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", idx ++), get_extents(shell_before)); |  | ||||||
|                 // Source shell.
 |                 // Source shell.
 | ||||||
|                 svg.draw(union_ex(shell_before, true)); |                 svg.draw(union_ex(shell_before, true)); | ||||||
|                 // Shell trimmed to the internal surfaces.
 |                 // Shell trimmed to the internal surfaces.
 | ||||||
|  | @ -743,26 +780,27 @@ PrintObject::discover_vertical_shells() | ||||||
| 
 | 
 | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|             { |             { | ||||||
|                 static size_t idx = 0; |                 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-%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", debug_idx), get_extents(shell), new_internal_void, "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", debug_idx), get_extents(shell), new_internal_solid, "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; |  | ||||||
|             } |             } | ||||||
| #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | ||||||
| 
 | 
 | ||||||
|             // Assign resulting internal surfaces to layer.
 |             // Assign resulting internal surfaces to layer.
 | ||||||
|             const SurfaceType surfaceTypesKeep[] = { stTop, stBottom, stBottomBridge }; |             const SurfaceType surfaceTypesKeep[] = { stTop, stBottom, stBottomBridge }; | ||||||
|             layerm->fill_surfaces.keep_types(surfaceTypesKeep, sizeof(surfaceTypesKeep)/sizeof(SurfaceType)); |             layerm->fill_surfaces.keep_types(surfaceTypesKeep, sizeof(surfaceTypesKeep)/sizeof(SurfaceType)); | ||||||
|             layerm->fill_surfaces.append(stInternal     , new_internal); |             layerm->fill_surfaces.append(new_internal,       stInternal); | ||||||
|             layerm->fill_surfaces.append(stInternalVoid , new_internal_void); |             layerm->fill_surfaces.append(new_internal_void,  stInternalVoid); | ||||||
|             layerm->fill_surfaces.append(stInternalSolid, new_internal_solid); |             layerm->fill_surfaces.append(new_internal_solid, stInternalSolid); | ||||||
|  |         } // for each layer
 | ||||||
| 
 | 
 | ||||||
| #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | #ifdef SLIC3R_DEBUG_SLICE_PROCESSING | ||||||
|             layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells"); | 		for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++idx_layer) { | ||||||
|             layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells"); | 			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 */ | #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ | ||||||
|         } // for each layer
 |  | ||||||
|     } // for each region
 |     } // for each region
 | ||||||
| 
 | 
 | ||||||
|     // Write the profiler measurements to file
 |     // Write the profiler measurements to file
 | ||||||
|  | @ -775,6 +813,8 @@ PrintObject::discover_vertical_shells() | ||||||
| void | void | ||||||
| PrintObject::bridge_over_infill() | PrintObject::bridge_over_infill() | ||||||
| { | { | ||||||
|  |     BOOST_LOG_TRIVIAL(info) << "Bridge over infill..."; | ||||||
|  | 
 | ||||||
|     FOREACH_REGION(this->_print, region) { |     FOREACH_REGION(this->_print, region) { | ||||||
|         size_t region_id = region - this->_print->regions.begin(); |         size_t region_id = region - this->_print->regions.begin(); | ||||||
|          |          | ||||||
|  | @ -846,7 +886,7 @@ PrintObject::bridge_over_infill() | ||||||
|             #endif |             #endif | ||||||
|              |              | ||||||
|             // compute the remaning internal solid surfaces as difference
 |             // 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); |             to_bridge = intersection_ex(to_polygons(to_bridge), internal_solid, true); | ||||||
|              |              | ||||||
|             // build the new collection of fill_surfaces
 |             // 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 | class SVG | ||||||
| { | { | ||||||
|     public: | public: | ||||||
|     bool arrows; |     bool arrows; | ||||||
|     std::string fill, stroke; |     std::string fill, stroke; | ||||||
|     Point origin; |     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 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)  |     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); } |         { 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 "Flow.hpp" | ||||||
| #include "PrintConfig.hpp" | #include "PrintConfig.hpp" | ||||||
|  | #include "Slicing.hpp" | ||||||
| 
 | 
 | ||||||
| namespace Slic3r { | namespace Slic3r { | ||||||
| 
 | 
 | ||||||
|  | @ -20,19 +21,33 @@ class PrintObjectConfig; | ||||||
| class PrintObjectSupportMaterial | class PrintObjectSupportMaterial | ||||||
| { | { | ||||||
| public: | 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 { | 	enum SupporLayerType { | ||||||
| 		sltUnknown = 0, | 		sltUnknown = 0, | ||||||
| 		sltRaft, | 		// Ratft base layer, to be printed with the support material.
 | ||||||
| 		stlFirstLayer, | 		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, | 		sltBottomContact, | ||||||
|  | 		// Dense interface layer, to be printed with the support interface material.
 | ||||||
|  | 		// This layer is separated from an object by an sltBottomContact layer.
 | ||||||
| 		sltBottomInterface, | 		sltBottomInterface, | ||||||
|  | 		// Sparse base support layer, to be printed with a support material.
 | ||||||
| 		sltBase, | 		sltBase, | ||||||
|  | 		// Dense interface layer, to be printed with the support interface material.
 | ||||||
|  | 		// This layer is separated from an object with sltTopContact layer.
 | ||||||
| 		sltTopInterface, | 		sltTopInterface, | ||||||
|  | 		// Top contact layer directly supporting an overhang. To be printed with a support interface material.
 | ||||||
| 		sltTopContact, | 		sltTopContact, | ||||||
| 		// Some undecided type yet. It will turn into stlBase first, then it may turn into stlBottomInterface or stlTopInterface.
 | 		// Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface.
 | ||||||
| 		stlIntermediate, | 		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 | 	class MyLayer | ||||||
| 	{ | 	{ | ||||||
| 	public: | 	public: | ||||||
|  | @ -57,6 +72,7 @@ public: | ||||||
| 			return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging; | 			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 { | 		bool operator<(const MyLayer &layer2) const { | ||||||
| 			if (print_z < layer2.print_z) { | 			if (print_z < layer2.print_z) { | ||||||
| 				return true; | 				return true; | ||||||
|  | @ -72,12 +88,12 @@ public: | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		SupporLayerType layer_type; | 		SupporLayerType layer_type; | ||||||
| 		// Z used for printing in unscaled coordinates
 | 		// Z used for printing, in unscaled coordinates.
 | ||||||
| 		coordf_t print_z; | 		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.
 | 		// otherwise bottom_z + gap + height = print_z.
 | ||||||
| 		coordf_t bottom_z; | 		coordf_t bottom_z; | ||||||
| 		// layer height in unscaled coordinates
 | 		// Layer height in unscaled coordinates.
 | ||||||
|     	coordf_t height; |     	coordf_t height; | ||||||
|     	// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
 |     	// 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).
 |     	// 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 to be filled by the support pattern.
 | ||||||
|     	Polygons polygons; |     	Polygons polygons; | ||||||
|     	// Currently for the contact layers only: Overhangs are stored here.
 |     	// Currently for the contact layers only: Overhangs are stored here.
 | ||||||
|  |     	// MyLayer owns the aux_polygons, they are freed by the destructor.
 | ||||||
|     	Polygons *aux_polygons; |     	Polygons *aux_polygons; | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	struct LayerExtreme | 	// 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, 
 | ||||||
| 		LayerExtreme(MyLayer *alayer, bool ais_top) : layer(alayer), is_top(ais_top) {} | 	// which would allocate layers by multiple chunks.
 | ||||||
| 		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; |  | ||||||
| 	typedef std::deque<MyLayer> 				MyLayerStorage; | 	typedef std::deque<MyLayer> 				MyLayerStorage; | ||||||
|  | 	typedef std::vector<MyLayer*> 				MyLayersPtr; | ||||||
| 
 | 
 | ||||||
| public: | 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
 | 	// Height of the 1st layer is user configured as it is important for the print
 | ||||||
| 	// to stick to he print bed.
 | 	// to stick to he print bed.
 | ||||||
| 	coordf_t	first_layer_height() 		const { return m_object_config->first_layer_height.value; } | 	coordf_t	first_layer_height() 		const { return m_object_config->first_layer_height.value; } | ||||||
| 
 | 
 | ||||||
| 	// Is raft enabled?
 | 	// Is raft enabled?
 | ||||||
| 	bool 		has_raft() 					const { return m_has_raft; } | 	bool 		has_raft() 					const { return m_slicing_params.has_raft(); } | ||||||
| 	// Has any support?
 | 	// Has any support?
 | ||||||
| 	bool 		has_support()				const { return m_object_config->support_material.value; } | 	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?
 | 	bool 		synchronize_layers()		const { return m_object_config->support_material_synchronize_layers.value; } | ||||||
| 	// The 1st object layer_id will be offsetted by this number.
 | 	bool 		has_contact_loops() 		const { return m_object_config->support_material_interface_contact_loops.value; } | ||||||
| 	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; } |  | ||||||
| 
 | 
 | ||||||
| 	// Generate support material for the object.
 | 	// Generate support material for the object.
 | ||||||
| 	// New support layers will be added to 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.
 | 	// Generate bottom contact layers supporting the top contact layers.
 | ||||||
| 	// For a soluble interface material synchronize the layer heights with the object, 
 | 	// 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.
 | 	// 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.
 | 	// 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; | 	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, | 	    MyLayerStorage	 	&layer_storage, | ||||||
| 	    const coordf_t       max_object_layer_height) const; | 	    const coordf_t       max_object_layer_height) const; | ||||||
| 
 | 
 | ||||||
|  | 	// Fill in the base layers with polygons.
 | ||||||
| 	void generate_base_layers( | 	void generate_base_layers( | ||||||
| 	    const PrintObject   &object, | 	    const PrintObject   &object, | ||||||
| 	    const MyLayersPtr   &bottom_contacts, | 	    const MyLayersPtr   &bottom_contacts, | ||||||
| 	    const MyLayersPtr   &top_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 PrintObject   &object, | ||||||
| 	    const MyLayersPtr   &bottom_contacts, | 	    const MyLayersPtr   &top_contacts, | ||||||
| 	    MyLayersPtr         &intermediate_layers) const; | 	    MyLayersPtr         &intermediate_layers, | ||||||
|  | 	    MyLayerStorage	 	&layer_storage) const; | ||||||
| 
 | 
 | ||||||
|  |     // Turn some of the base layers into interface layers.
 | ||||||
| 	MyLayersPtr generate_interface_layers( | 	MyLayersPtr generate_interface_layers( | ||||||
| 	    const PrintObject   &object, | 	    const PrintObject   &object, | ||||||
| 	    const MyLayersPtr   &bottom_contacts, | 	    const MyLayersPtr   &bottom_contacts, | ||||||
|  | @ -194,6 +186,15 @@ private: | ||||||
| 	    MyLayersPtr         &intermediate_layers, | 	    MyLayersPtr         &intermediate_layers, | ||||||
| 	    MyLayerStorage      &layer_storage) const; | 	    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 generate_pillars_shape(); | ||||||
| 	void clip_with_shape(); | 	void clip_with_shape(); | ||||||
|  | @ -202,59 +203,28 @@ private: | ||||||
| 	// Produce the actual G-code.
 | 	// Produce the actual G-code.
 | ||||||
| 	void generate_toolpaths( | 	void generate_toolpaths( | ||||||
|         const PrintObject   &object, |         const PrintObject   &object, | ||||||
|         const Polygons 		&raft, |         const MyLayersPtr 	&raft_layers, | ||||||
|         const MyLayersPtr   &bottom_contacts, |         const MyLayersPtr   &bottom_contacts, | ||||||
|         const MyLayersPtr   &top_contacts, |         const MyLayersPtr   &top_contacts, | ||||||
|         const MyLayersPtr   &intermediate_layers, |         const MyLayersPtr   &intermediate_layers, | ||||||
|         const MyLayersPtr   &interface_layers) const; |         const MyLayersPtr   &interface_layers) const; | ||||||
| 
 | 
 | ||||||
|  | 	// Following objects are not owned by SupportMaterial class.
 | ||||||
| 	const PrintObject 		*m_object; | 	const PrintObject 		*m_object; | ||||||
| 	const PrintConfig 		*m_print_config; | 	const PrintConfig 		*m_print_config; | ||||||
| 	const PrintObjectConfig *m_object_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_first_layer_flow; | ||||||
| 	Flow 			 	 m_support_material_flow; | 	Flow 			 	 m_support_material_flow; | ||||||
| 	Flow 			 	 m_support_material_interface_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_min; | ||||||
| 	coordf_t		 	 m_support_layer_height_max; | 	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_xy; | ||||||
| 	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
 | } // namespace Slic3r
 | ||||||
|  |  | ||||||
|  | @ -57,6 +57,7 @@ public: | ||||||
|     operator Polygons() const; |     operator Polygons() const; | ||||||
|     double area() const; |     double area() const; | ||||||
|     bool empty() const { return expolygon.empty(); } |     bool empty() const { return expolygon.empty(); } | ||||||
|  |     void clear() { expolygon.clear(); } | ||||||
|     bool is_solid() const; |     bool is_solid() const; | ||||||
|     bool is_external() const; |     bool is_external() const; | ||||||
|     bool is_internal() const; |     bool is_internal() const; | ||||||
|  |  | ||||||
|  | @ -8,22 +8,12 @@ namespace Slic3r { | ||||||
| 
 | 
 | ||||||
| SurfaceCollection::operator Polygons() const | SurfaceCollection::operator Polygons() const | ||||||
| { | { | ||||||
|     Polygons polygons; | 	return to_polygons(surfaces); | ||||||
|     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; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SurfaceCollection::operator ExPolygons() const | SurfaceCollection::operator ExPolygons() const | ||||||
| { | { | ||||||
|     ExPolygons expp; | 	return to_expolygons(surfaces); | ||||||
|     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; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
|  | @ -196,19 +186,6 @@ SurfaceCollection::remove_types(const SurfaceType *types, int ntypes) | ||||||
|         surfaces.erase(surfaces.begin() + j, surfaces.end()); |         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)  | void SurfaceCollection::export_to_svg(const char *path, bool show_labels)  | ||||||
| { | { | ||||||
|     BoundingBox bbox; |     BoundingBox bbox; | ||||||
|  |  | ||||||
|  | @ -28,8 +28,27 @@ class SurfaceCollection | ||||||
|     void remove_type(const SurfaceType type); |     void remove_type(const SurfaceType type); | ||||||
|     void remove_types(const SurfaceType *types, int ntypes); |     void remove_types(const SurfaceType *types, int ntypes); | ||||||
|     void filter_by_type(SurfaceType type, Polygons* polygons); |     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:
 |     // For debugging purposes:
 | ||||||
|     void export_to_svg(const char *path, bool show_labels); |     void export_to_svg(const char *path, bool show_labels); | ||||||
|  |  | ||||||
|  | @ -2,8 +2,8 @@ | ||||||
| #include "ClipperUtils.hpp" | #include "ClipperUtils.hpp" | ||||||
| #include "Geometry.hpp" | #include "Geometry.hpp" | ||||||
| #include <cmath> | #include <cmath> | ||||||
| #include <queue> |  | ||||||
| #include <deque> | #include <deque> | ||||||
|  | #include <queue> | ||||||
| #include <set> | #include <set> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <map> | #include <map> | ||||||
|  | @ -193,12 +193,17 @@ void TriangleMesh::scale(const Pointf3 &versor) | ||||||
| 
 | 
 | ||||||
| void TriangleMesh::translate(float x, float y, float z) | 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_translate_relative(&(this->stl), x, y, z); | ||||||
|     stl_invalidate_shared_vertices(&this->stl); |     stl_invalidate_shared_vertices(&this->stl); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TriangleMesh::rotate(float angle, const Axis &axis) | void TriangleMesh::rotate(float angle, const Axis &axis) | ||||||
| { | { | ||||||
|  |     if (angle == 0.f) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|     // admesh uses degrees
 |     // admesh uses degrees
 | ||||||
|     angle = Slic3r::Geometry::rad2deg(angle); |     angle = Slic3r::Geometry::rad2deg(angle); | ||||||
|      |      | ||||||
|  | @ -265,6 +270,8 @@ void TriangleMesh::align_to_origin() | ||||||
| 
 | 
 | ||||||
| void TriangleMesh::rotate(double angle, Point* center) | void TriangleMesh::rotate(double angle, Point* center) | ||||||
| { | { | ||||||
|  |     if (angle == 0.) | ||||||
|  |         return; | ||||||
|     this->translate(-center->x, -center->y, 0); |     this->translate(-center->x, -center->y, 0); | ||||||
|     stl_rotate_z(&(this->stl), (float)angle); |     stl_rotate_z(&(this->stl), (float)angle); | ||||||
|     this->translate(+center->x, +center->y, 0); |     this->translate(+center->x, +center->y, 0); | ||||||
|  | @ -363,10 +370,7 @@ TriangleMesh::horizontal_projection() const | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     // the offset factor was tuned using groovemount.stl
 |     // the offset factor was tuned using groovemount.stl
 | ||||||
|     offset(pp, &pp, 0.01 / SCALING_FACTOR); |     return union_ex(offset(pp, 0.01 / SCALING_FACTOR), true); | ||||||
|     ExPolygons retval; |  | ||||||
|     union_(pp, &retval, true); |  | ||||||
|     return retval; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Polygon | Polygon | ||||||
|  | @ -403,7 +407,7 @@ TriangleMesh::require_shared_vertices() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | 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 |        This method gets called with a list of unscaled Z coordinates and outputs | ||||||
|  | @ -427,58 +431,66 @@ 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  |         At the end, we free the tables generated by analyze() as we don't  | ||||||
|         need them anymore. |         need them anymore. | ||||||
|         FUTURE: parallelize slice_facet() and make_loops() |  | ||||||
|          |          | ||||||
|         NOTE: this method accepts a vector of floats because the mesh coordinate |         NOTE: this method accepts a vector of floats because the mesh coordinate | ||||||
|         type is float. |         type is float. | ||||||
|     */ |     */ | ||||||
|      |      | ||||||
|     std::vector<IntersectionLines> lines(z.size()); |     std::vector<IntersectionLines> lines(z.size()); | ||||||
|      |     { | ||||||
|     for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { |         boost::mutex lines_mutex; | ||||||
|         stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; |         parallelize<int>( | ||||||
|          |             0, | ||||||
|         // find facet extents
 |             this->mesh->stl.stats.number_of_facets-1, | ||||||
|         float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z)); |             boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z) | ||||||
|         float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); |         ); | ||||||
|          |  | ||||||
|         #ifdef SLIC3R_TRIANGLEMESH_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); |  | ||||||
|         printf("z: min = %.2f, max = %.2f\n", min_z, max_z); |  | ||||||
|         #endif |  | ||||||
|          |  | ||||||
|         // find layer extents
 |  | ||||||
|         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 |  | ||||||
|         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
 |     // v_scaled_shared could be freed here
 | ||||||
|      |      | ||||||
|     // build loops
 |     // build loops
 | ||||||
|     layers->resize(z.size()); |     layers->resize(z.size()); | ||||||
|     for (std::vector<IntersectionLines>::iterator it = lines.begin(); it != lines.end(); ++it) { |     parallelize<size_t>( | ||||||
|         size_t layer_idx = it - lines.begin(); |         0, | ||||||
|         #ifdef SLIC3R_TRIANGLEMESH_DEBUG |         lines.size()-1, | ||||||
|         printf("Layer " PRINTF_ZU ":\n", layer_idx); |         boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers) | ||||||
|         #endif |     ); | ||||||
|         this->make_loops(*it, &(*layers)[layer_idx]); | } | ||||||
|  | 
 | ||||||
|  | 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
 | ||||||
|  |     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_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); | ||||||
|  |     printf("z: min = %.2f, max = %.2f\n", min_z, max_z); | ||||||
|  |     #endif | ||||||
|  |      | ||||||
|  |     // find layer extents
 | ||||||
|  |     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_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], lines_mutex); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | 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; |     std::vector<Polygons> layers_p; | ||||||
|     this->slice(z, &layers_p); |     this->slice(z, &layers_p); | ||||||
|  | @ -495,7 +507,9 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | 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<IntersectionPoint> points; | ||||||
|     std::vector< std::vector<IntersectionPoint>::size_type > points_on_layer; |     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.b.y    = b->y; | ||||||
|             line.a_id   = a_id; |             line.a_id   = a_id; | ||||||
|             line.b_id   = b_id; |             line.b_id   = b_id; | ||||||
|             lines->push_back(line); |             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; |             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.b_id       = points[0].point_id; | ||||||
|         line.edge_a_id  = points[1].edge_id; |         line.edge_a_id  = points[1].edge_id; | ||||||
|         line.edge_b_id  = points[0].edge_id; |         line.edge_b_id  = points[0].edge_id; | ||||||
|         lines->push_back(line); |         if (lines_mutex != NULL) { | ||||||
|  |             boost::lock_guard<boost::mutex> l(*lines_mutex); | ||||||
|  |             lines->push_back(line); | ||||||
|  |         } else { | ||||||
|  |             lines->push_back(line); | ||||||
|  |         } | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | 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"); |     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) { |                     for (IntersectionLinePtrs::const_iterator lineptr = loop.begin(); lineptr != loop.end(); ++lineptr) { | ||||||
|                         p.points.push_back((*lineptr)->a); |                         p.points.push_back((*lineptr)->a); | ||||||
|                     } |                     } | ||||||
|  |                      | ||||||
|                     loops->push_back(p); |                     loops->push_back(p); | ||||||
|                      |                      | ||||||
|                     #ifdef SLIC3R_TRIANGLEMESH_DEBUG |                     #ifdef SLIC3R_TRIANGLEMESH_DEBUG | ||||||
|  | @ -746,7 +777,7 @@ class _area_comp { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
| TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) | TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const | ||||||
| { | { | ||||||
|     Polygons loops; |     Polygons loops; | ||||||
|     this->make_loops(lines, &loops); |     this->make_loops(lines, &loops); | ||||||
|  | @ -780,7 +811,7 @@ TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | 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 |         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 |            of the loops, since the Orientation() function provided by Clipper | ||||||
|            would do the same, thus repeating the calculation */ |            would do the same, thus repeating the calculation */ | ||||||
|         Polygons::const_iterator loop = loops.begin() + *loop_idx; |         Polygons::const_iterator loop = loops.begin() + *loop_idx; | ||||||
|         if (area[*loop_idx] > +EPSILON) { |         if (area[*loop_idx] > +EPSILON) | ||||||
|             p_slices.push_back(*loop); |             p_slices.push_back(*loop); | ||||||
|         } else if (area[*loop_idx] < -EPSILON) { |         else if (area[*loop_idx] < -EPSILON) | ||||||
|             diff(p_slices, *loop, &p_slices); |             p_slices = diff(p_slices, *loop); | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // perform a safety offset to merge very close facets (TODO: find test case for this)
 |     // perform a safety offset to merge very close facets (TODO: find test case for this)
 | ||||||
|     double safety_offset = scale_(0.0499); |     double safety_offset = scale_(0.0499); | ||||||
|     ExPolygons ex_slices; |     ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset); | ||||||
|     offset2(p_slices, &ex_slices, +safety_offset, -safety_offset); |  | ||||||
|      |      | ||||||
|     #ifdef SLIC3R_TRIANGLEMESH_DEBUG |     #ifdef SLIC3R_TRIANGLEMESH_DEBUG | ||||||
|     size_t holes_count = 0; |     size_t holes_count = 0; | ||||||
|  | @ -840,11 +869,11 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) | ||||||
|     #endif |     #endif | ||||||
|      |      | ||||||
|     // append to the supplied collection
 |     // append to the supplied collection
 | ||||||
|     slices->insert(slices->end(), ex_slices.begin(), ex_slices.end()); |     expolygons_append(*slices, ex_slices); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
| TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) | TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const | ||||||
| { | { | ||||||
|     Polygons pp; |     Polygons pp; | ||||||
|     this->make_loops(lines, &pp); |     this->make_loops(lines, &pp); | ||||||
|  | @ -852,7 +881,7 @@ TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPoly | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
| TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) | TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const | ||||||
| { | { | ||||||
|     IntersectionLines upper_lines, lower_lines; |     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(&(upper->stl)); | ||||||
|     stl_get_size(&(lower->stl)); |     stl_get_size(&(lower->stl)); | ||||||
|      |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL) | TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL) | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| #include "libslic3r.h" | #include "libslic3r.h" | ||||||
| #include <admesh/stl.h> | #include <admesh/stl.h> | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include <boost/thread.hpp> | ||||||
| #include "BoundingBox.hpp" | #include "BoundingBox.hpp" | ||||||
| #include "Line.hpp" | #include "Line.hpp" | ||||||
| #include "Point.hpp" | #include "Point.hpp" | ||||||
|  | @ -88,19 +89,23 @@ class TriangleMeshSlicer | ||||||
|     TriangleMesh* mesh; |     TriangleMesh* mesh; | ||||||
|     TriangleMeshSlicer(TriangleMesh* _mesh); |     TriangleMeshSlicer(TriangleMesh* _mesh); | ||||||
|     ~TriangleMeshSlicer(); |     ~TriangleMeshSlicer(); | ||||||
|     void slice(const std::vector<float> &z, std::vector<Polygons>* layers); |     void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const; | ||||||
|     void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers); |     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) const; |     void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, | ||||||
|     void cut(float z, TriangleMesh* upper, TriangleMesh* lower); |         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: |     private: | ||||||
|     typedef std::vector< std::vector<int> > t_facets_edges; |     typedef std::vector< std::vector<int> > t_facets_edges; | ||||||
|     t_facets_edges facets_edges; |     t_facets_edges facets_edges; | ||||||
|     stl_vertex* v_scaled_shared; |     stl_vertex* v_scaled_shared; | ||||||
|     void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops); |     void _slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, const std::vector<float> &z) const; | ||||||
|     void make_expolygons(const Polygons &loops, ExPolygons* slices); |     void _make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const; | ||||||
|     void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices); |     void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const; | ||||||
|     void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices); |     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)
 | // this needs to be included early for MSVC (listing it in Build.PL is not enough)
 | ||||||
| #include <ostream> | #include <ostream> | ||||||
| #include <iostream> | #include <iostream> | ||||||
|  | #include <math.h> | ||||||
|  | #include <queue> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <cstdio> | #include <cstdio> | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
|  | #include <vector> | ||||||
|  | #include <boost/thread.hpp> | ||||||
| 
 | 
 | ||||||
| #define SLIC3R_FORK_NAME "Slic3r Prusa Edition" | #define SLIC3R_FORK_NAME "Slic3r Prusa Edition" | ||||||
| #define SLIC3R_VERSION "1.31.6" | #define SLIC3R_VERSION "1.31.6" | ||||||
|  | @ -40,13 +44,6 @@ | ||||||
| typedef long coord_t; | typedef long coord_t; | ||||||
| typedef double coordf_t; | typedef double coordf_t; | ||||||
| 
 | 
 | ||||||
| namespace Slic3r { |  | ||||||
| 
 |  | ||||||
| enum Axis { X=0, Y, Z }; |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| using namespace Slic3r; |  | ||||||
| 
 |  | ||||||
| /* Implementation of CONFESS("foo"): */ | /* Implementation of CONFESS("foo"): */ | ||||||
| #ifdef _MSC_VER | #ifdef _MSC_VER | ||||||
| 	#define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) | 	#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.
 | // Write slices as SVG images into out directory during the 2D processing of the slices.
 | ||||||
| // #define SLIC3R_DEBUG_SLICE_PROCESSING
 | // #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 | #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 | #ifdef SLIC3R_HAS_BROKEN_CROAK | ||||||
| 
 | 
 | ||||||
| // Some Strawberry Perl builds (mainly the latest 64bit builds) have a broken mechanism
 | // Some Strawberry Perl builds (mainly the latest 64bit builds) have a broken mechanism
 | ||||||
|  |  | ||||||
|  | @ -532,8 +532,7 @@ SV* | ||||||
| polynode2perl(const ClipperLib::PolyNode& node) | polynode2perl(const ClipperLib::PolyNode& node) | ||||||
| { | { | ||||||
|     HV* hv = newHV(); |     HV* hv = newHV(); | ||||||
|     Slic3r::Polygon p; |     Slic3r::Polygon p = ClipperPath_to_Slic3rPolygon(node.Contour); | ||||||
|     ClipperPath_to_Slic3rMultiPoint(node.Contour, &p); |  | ||||||
|     if (node.IsHole()) { |     if (node.IsHole()) { | ||||||
|         (void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) ); |         (void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) ); | ||||||
|     } else { |     } else { | ||||||
|  |  | ||||||
|  | @ -31,6 +31,7 @@ | ||||||
| #include <ostream> | #include <ostream> | ||||||
| #include <iostream> | #include <iostream> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  | #include <libslic3r.h> | ||||||
| 
 | 
 | ||||||
| #ifdef SLIC3RXS | #ifdef SLIC3RXS | ||||||
| extern "C" { | extern "C" { | ||||||
|  | @ -42,15 +43,18 @@ extern "C" { | ||||||
| #undef do_close | #undef do_close | ||||||
| #undef bind | #undef bind | ||||||
| #undef seed | #undef seed | ||||||
|  | #undef push | ||||||
|  | #undef pop | ||||||
| #ifdef _MSC_VER | #ifdef _MSC_VER | ||||||
|     // Undef some of the macros set by Perl <xsinit.h>, which cause compilation errors on Win32
 |     // Undef some of the macros set by Perl <xsinit.h>, which cause compilation errors on Win32
 | ||||||
|     #undef send |  | ||||||
|     #undef connect |     #undef connect | ||||||
|  |     #undef seek | ||||||
|  |     #undef send | ||||||
|  |     #undef write | ||||||
| #endif /* _MSC_VER */ | #endif /* _MSC_VER */ | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include <libslic3r.h> |  | ||||||
| #include <ClipperUtils.hpp> | #include <ClipperUtils.hpp> | ||||||
| #include <Config.hpp> | #include <Config.hpp> | ||||||
| #include <ExPolygon.hpp> | #include <ExPolygon.hpp> | ||||||
|  | @ -163,4 +167,6 @@ SV* polynode2perl(const ClipperLib::PolyNode& node); | ||||||
| #endif | #endif | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | using namespace Slic3r; | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ use warnings; | ||||||
| 
 | 
 | ||||||
| use List::Util qw(sum); | use List::Util qw(sum); | ||||||
| use Slic3r::XS; | use Slic3r::XS; | ||||||
| use Test::More tests => 23; | use Test::More tests => 16; | ||||||
| 
 | 
 | ||||||
| my $square = Slic3r::Polygon->new(  # ccw | my $square = Slic3r::Polygon->new(  # ccw | ||||||
|     [200, 100], |     [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'; |     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) |     # Clipper bug #96 (our issue #2028) | ||||||
|     my $subject = Slic3r::Polyline->new( |     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'; |     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 |     # Clipper bug #122 | ||||||
|     my $subject = [ |     my $subject = [ | ||||||
|  |  | ||||||
|  | @ -30,7 +30,8 @@ | ||||||
|     long y_min() %code{% RETVAL = THIS->min.y; %}; |     long y_min() %code{% RETVAL = THIS->min.y; %}; | ||||||
|     long y_max() %code{% RETVAL = THIS->max.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; %}; |     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; %}; | ||||||
|  | 
 | ||||||
| %{ | %{ | ||||||
| 
 | 
 | ||||||
| BoundingBox* | BoundingBox* | ||||||
|  | @ -69,7 +70,8 @@ new_from_points(CLASS, points) | ||||||
|     void set_y_min(double val) %code{% THIS->min.y = val; %}; |     void set_y_min(double val) %code{% THIS->min.y = val; %}; | ||||||
|     void set_y_max(double val) %code{% THIS->max.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; %}; |     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; %}; | ||||||
|  |      | ||||||
| %{ | %{ | ||||||
| 
 | 
 | ||||||
| BoundingBoxf* | BoundingBoxf* | ||||||
|  | @ -106,4 +108,5 @@ new_from_points(CLASS, points) | ||||||
|     double z_min() %code{% RETVAL = THIS->min.z; %}; |     double z_min() %code{% RETVAL = THIS->min.z; %}; | ||||||
|     double z_max() %code{% RETVAL = THIS->max.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; %}; |     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_MITER        = jtMiter | ||||||
|     JT_ROUND        = jtRound |     JT_ROUND        = jtRound | ||||||
|     JT_SQUARE       = jtSquare |     JT_SQUARE       = jtSquare | ||||||
|     CLIPPER_OFFSET_SCALE = CLIPPER_OFFSET_SCALE |  | ||||||
|   CODE: |   CODE: | ||||||
|     RETVAL = ix; |     RETVAL = ix; | ||||||
|   OUTPUT: RETVAL |   OUTPUT: RETVAL | ||||||
| 
 | 
 | ||||||
| Polygons | Polygons | ||||||
| offset(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) | offset(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||||
|     Polygons                polygons |     Polygons                polygons | ||||||
|     const float             delta |     const float             delta | ||||||
|     double                  scale |  | ||||||
|     ClipperLib::JoinType    joinType |     ClipperLib::JoinType    joinType | ||||||
|     double                  miterLimit |     double                  miterLimit | ||||||
|     CODE: |     CODE: | ||||||
|         offset(polygons, &RETVAL, delta, scale, joinType, miterLimit); |         RETVAL = offset(polygons, delta, joinType, miterLimit); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
| ExPolygons | 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 |     Polygons                polygons | ||||||
|     const float             delta |     const float             delta | ||||||
|     double                  scale |  | ||||||
|     ClipperLib::JoinType    joinType |     ClipperLib::JoinType    joinType | ||||||
|     double                  miterLimit |     double                  miterLimit | ||||||
|     CODE: |     CODE: | ||||||
|         offset(polygons, &RETVAL, delta, scale, joinType, miterLimit); |         RETVAL = offset_ex(polygons, delta, joinType, miterLimit); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
| Polygons | 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 |     Polygons                polygons | ||||||
|     const float             delta1 |     const float             delta1 | ||||||
|     const float             delta2 |     const float             delta2 | ||||||
|     double                  scale |  | ||||||
|     ClipperLib::JoinType    joinType |     ClipperLib::JoinType    joinType | ||||||
|     double                  miterLimit |     double                  miterLimit | ||||||
|     CODE: |     CODE: | ||||||
|         offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit); |         RETVAL = offset2(polygons, delta1, delta2, joinType, miterLimit); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
| ExPolygons | 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 |     Polygons                polygons | ||||||
|     const float             delta1 |     const float             delta1 | ||||||
|     const float             delta2 |     const float             delta2 | ||||||
|     double                  scale |  | ||||||
|     ClipperLib::JoinType    joinType |     ClipperLib::JoinType    joinType | ||||||
|     double                  miterLimit |     double                  miterLimit | ||||||
|     CODE: |     CODE: | ||||||
|         offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit); |         RETVAL = offset2_ex(polygons, delta1, delta2, joinType, miterLimit); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  | @ -77,7 +72,7 @@ diff(subject, clip, safety_offset = false) | ||||||
|     Polygons    clip |     Polygons    clip | ||||||
|     bool        safety_offset |     bool        safety_offset | ||||||
|     CODE: |     CODE: | ||||||
|         diff(subject, clip, &RETVAL, safety_offset); |         RETVAL = diff(subject, clip, safety_offset); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  | @ -87,7 +82,7 @@ diff_ex(subject, clip, safety_offset = false) | ||||||
|     Polygons    clip |     Polygons    clip | ||||||
|     bool        safety_offset |     bool        safety_offset | ||||||
|     CODE: |     CODE: | ||||||
|         diff(subject, clip, &RETVAL, safety_offset); |         RETVAL = diff_ex(subject, clip, safety_offset); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  | @ -96,16 +91,7 @@ diff_pl(subject, clip) | ||||||
|     Polylines   subject |     Polylines   subject | ||||||
|     Polygons    clip |     Polygons    clip | ||||||
|     CODE: |     CODE: | ||||||
|         diff(subject, clip, &RETVAL); |         RETVAL = diff_pl(subject, clip); | ||||||
|     OUTPUT: |  | ||||||
|         RETVAL |  | ||||||
| 
 |  | ||||||
| Polylines |  | ||||||
| diff_ppl(subject, clip) |  | ||||||
|     Polygons    subject |  | ||||||
|     Polygons    clip |  | ||||||
|     CODE: |  | ||||||
|         diff(subject, clip, &RETVAL); |  | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  | @ -115,7 +101,7 @@ intersection(subject, clip, safety_offset = false) | ||||||
|     Polygons                    clip |     Polygons                    clip | ||||||
|     bool                        safety_offset |     bool                        safety_offset | ||||||
|     CODE: |     CODE: | ||||||
|         intersection(subject, clip, &RETVAL, safety_offset); |         RETVAL = intersection(subject, clip, safety_offset); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  | @ -125,7 +111,7 @@ intersection_ex(subject, clip, safety_offset = false) | ||||||
|     Polygons                    clip |     Polygons                    clip | ||||||
|     bool                        safety_offset |     bool                        safety_offset | ||||||
|     CODE: |     CODE: | ||||||
|         intersection(subject, clip, &RETVAL, safety_offset); |         RETVAL = intersection_ex(subject, clip, safety_offset); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  | @ -134,26 +120,7 @@ intersection_pl(subject, clip) | ||||||
|     Polylines                   subject |     Polylines                   subject | ||||||
|     Polygons                    clip |     Polygons                    clip | ||||||
|     CODE: |     CODE: | ||||||
|         intersection(subject, clip, &RETVAL); |         RETVAL = intersection_pl(subject, clip); | ||||||
|     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); |  | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  | @ -162,7 +129,7 @@ union(subject, safety_offset = false) | ||||||
|     Polygons    subject |     Polygons    subject | ||||||
|     bool        safety_offset |     bool        safety_offset | ||||||
|     CODE: |     CODE: | ||||||
|         union_(subject, &RETVAL, safety_offset); |         RETVAL = union_(subject, safety_offset); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  | @ -171,20 +138,7 @@ union_ex(subject, safety_offset = false) | ||||||
|     Polygons                    subject |     Polygons                    subject | ||||||
|     bool                        safety_offset |     bool                        safety_offset | ||||||
|     CODE: |     CODE: | ||||||
|         union_(subject, &RETVAL, safety_offset); |         RETVAL = union_ex(subject, 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); |  | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  | @ -193,7 +147,7 @@ union_pt_chained(subject, safety_offset = false) | ||||||
|     Polygons                    subject |     Polygons                    subject | ||||||
|     bool                        safety_offset |     bool                        safety_offset | ||||||
|     CODE: |     CODE: | ||||||
|         union_pt_chained(subject, &RETVAL, safety_offset); |         RETVAL = union_pt_chained(subject, safety_offset); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  | @ -201,7 +155,7 @@ Polygons | ||||||
| simplify_polygons(subject) | simplify_polygons(subject) | ||||||
|     Polygons                    subject |     Polygons                    subject | ||||||
|     CODE: |     CODE: | ||||||
|         simplify_polygons(subject, &RETVAL); |         RETVAL = simplify_polygons(subject); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -80,13 +80,12 @@ Polyline::rotate(angle, center_sv) | ||||||
|         THIS->rotate(angle, center); |         THIS->rotate(angle, center); | ||||||
| 
 | 
 | ||||||
| Polygons | Polygons | ||||||
| Polyline::grow(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtSquare, miterLimit = 3) | Polyline::grow(delta, joinType = ClipperLib::jtSquare, miterLimit = 3) | ||||||
|     const float             delta |     const float             delta | ||||||
|     double                  scale |  | ||||||
|     ClipperLib::JoinType    joinType |     ClipperLib::JoinType    joinType | ||||||
|     double                  miterLimit |     double                  miterLimit | ||||||
|     CODE: |     CODE: | ||||||
|         offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit); |         RETVAL = offset(*THIS, delta, joinType, miterLimit); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| %{ | %{ | ||||||
| #include <xsinit.h> | #include <xsinit.h> | ||||||
| #include "libslic3r/Print.hpp" | #include "libslic3r/Print.hpp" | ||||||
|  | #include "libslic3r/Slicing.hpp" | ||||||
| #include "libslic3r/PlaceholderParser.hpp" | #include "libslic3r/PlaceholderParser.hpp" | ||||||
| %} | %} | ||||||
| 
 | 
 | ||||||
|  | @ -58,6 +59,8 @@ _constant() | ||||||
|     Points copies(); |     Points copies(); | ||||||
|     t_layer_height_ranges layer_height_ranges() |     t_layer_height_ranges layer_height_ranges() | ||||||
|         %code%{ RETVAL = THIS->layer_height_ranges; %}; |         %code%{ RETVAL = THIS->layer_height_ranges; %}; | ||||||
|  |     std::vector<double> layer_height_profile() | ||||||
|  |         %code%{ RETVAL = THIS->layer_height_profile; %}; | ||||||
|     Ref<Point3> size() |     Ref<Point3> size() | ||||||
|         %code%{ RETVAL = &THIS->size; %}; |         %code%{ RETVAL = &THIS->size; %}; | ||||||
|     Clone<BoundingBox> bounding_box(); |     Clone<BoundingBox> bounding_box(); | ||||||
|  | @ -82,6 +85,8 @@ _constant() | ||||||
|     bool reload_model_instances(); |     bool reload_model_instances(); | ||||||
|     void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges) |     void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges) | ||||||
|         %code%{ THIS->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 total_layer_count(); | ||||||
|     size_t layer_count(); |     size_t layer_count(); | ||||||
|  | @ -106,11 +111,32 @@ _constant() | ||||||
|         %code%{ THIS->state.set_done(step); %}; |         %code%{ THIS->state.set_done(step); %}; | ||||||
|     void set_step_started(PrintObjectStep step) |     void set_step_started(PrintObjectStep step) | ||||||
|         %code%{ THIS->state.set_started(step); %}; |         %code%{ THIS->state.set_started(step); %}; | ||||||
|      | 
 | ||||||
|  |     void _slice(); | ||||||
|     void detect_surfaces_type(); |     void detect_surfaces_type(); | ||||||
|     void process_external_surfaces(); |     void process_external_surfaces(); | ||||||
|     void discover_vertical_shells(); |     void discover_vertical_shells(); | ||||||
|     void bridge_over_infill(); |     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() |     int ptr() | ||||||
|         %code%{ RETVAL = (int)(intptr_t)THIS; %}; |         %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 |         RETVAL | ||||||
| 
 | 
 | ||||||
| Surfaces | Surfaces | ||||||
| Surface::offset(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) | Surface::offset(delta, joinType = ClipperLib::jtMiter, miterLimit = 3) | ||||||
|     const float             delta |     const float             delta | ||||||
|     double                  scale |  | ||||||
|     ClipperLib::JoinType    joinType |     ClipperLib::JoinType    joinType | ||||||
|     double                  miterLimit |     double                  miterLimit | ||||||
|     CODE: |     CODE: | ||||||
|         offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit); |         surfaces_append(RETVAL, offset_ex(THIS->expolygon, delta, joinType, miterLimit), *THIS); | ||||||
|     OUTPUT: |     OUTPUT: | ||||||
|         RETVAL |         RETVAL | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ | ||||||
| %package{Slic3r::XS}; | %package{Slic3r::XS}; | ||||||
| 
 | 
 | ||||||
| #include <xsinit.h> | #include <xsinit.h> | ||||||
|  | #include "Utils.hpp" | ||||||
| 
 | 
 | ||||||
| %{ | %{ | ||||||
| 
 | 
 | ||||||
|  | @ -28,6 +29,12 @@ FORK_NAME() | ||||||
|         RETVAL = newSVpv(SLIC3R_FORK_NAME, 0); |         RETVAL = newSVpv(SLIC3R_FORK_NAME, 0); | ||||||
|     OUTPUT: RETVAL |     OUTPUT: RETVAL | ||||||
| 
 | 
 | ||||||
|  | void | ||||||
|  | set_logging_level(level) | ||||||
|  |     unsigned int level; | ||||||
|  |     CODE: | ||||||
|  |         Slic3r::set_logging_level(level); | ||||||
|  | 
 | ||||||
| void | void | ||||||
| xspp_test_croak_hangs_on_strawberry() | xspp_test_croak_hangs_on_strawberry() | ||||||
|     CODE: |     CODE: | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Eyal Soha
						Eyal Soha