Change char to int

char might be signed or unsigned but int is definitely signed.  This fixes  .
This commit is contained in:
Eyal Soha 2017-01-12 10:59:33 +02:00
commit b851e04c17
90 changed files with 4977 additions and 2964 deletions

View file

@ -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) = @_;

View file

@ -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;

View file

@ -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
@ -131,6 +140,15 @@ sub new {
$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;
EVT_PAINT($self, sub { EVT_PAINT($self, sub {
@ -144,57 +162,110 @@ 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.
# Start editing the layer height.
$self->_layer_height_edited(1);
$self->_variable_layer_thickness_action($e, 1);
} else {
# Select volume in this 3D canvas.
# Don't deselect a volume if layer editing is enabled. We want the object to stay selected
# during the scene manipulation.
if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) {
$self->deselect_volumes; $self->deselect_volumes;
$self->select_volume($volume_idx); $self->select_volume($volume_idx);
@ -225,7 +296,8 @@ sub mouse_event {
if $self->on_right_click; if $self->on_right_click;
} }
} }
} elsif ($e->Dragging && $e->LeftIsDown && defined($self->_drag_volume_idx)) { }
} elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) {
# get new position at the same Z of the initial click point # 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) = @_;
@ -721,6 +842,21 @@ sub InitGL {
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);
@ -762,6 +898,13 @@ sub InitGL {
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) = @_;
@ -799,10 +942,14 @@ sub Render {
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
@ -948,9 +1096,12 @@ sub Render {
glDisable(GL_BLEND); glDisable(GL_BLEND);
} }
glFlush(); $self->draw_active_object_annotations;
$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')) {
@ -1153,6 +1590,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}) {
my $volume = $model_object->volumes->[$volume_idx]; my $volume = $model_object->volumes->[$volume_idx];
@ -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
View 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__

View file

@ -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' ); });

View file

@ -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);
@ -1482,6 +1505,8 @@ sub on_thumbnail_made {
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);

View file

@ -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);

View file

@ -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;

View file

@ -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],

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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(

View file

@ -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;

View file

@ -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,20 +260,10 @@ 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);
return;
}
$self->print->status_cb->(85, "Generating support material"); $self->print->status_cb->(85, "Generating support material");
if (0) {
$self->_support_material->generate($self); # Old supports, Perl implementation.
$self->set_step_done(STEP_SUPPORTMATERIAL);
}
sub _support_material {
my ($self) = @_;
my $first_layer_flow = Slic3r::Flow->new_from_width( my $first_layer_flow = Slic3r::Flow->new_from_width(
width => ($self->print->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width), width => ($self->print->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
role => FLOW_ROLE_SUPPORT_MATERIAL, role => FLOW_ROLE_SUPPORT_MATERIAL,
@ -659,22 +272,23 @@ sub _support_material {
layer_height => $self->config->get_abs_value('first_layer_height'), layer_height => $self->config->get_abs_value('first_layer_height'),
bridge_flow_ratio => 0, bridge_flow_ratio => 0,
); );
my $support_material = Slic3r::Print::SupportMaterial->new(
if (1) {
# Old supports, Perl implementation.
return Slic3r::Print::SupportMaterial->new(
print_config => $self->print->config, print_config => $self->print->config,
object_config => $self->config, object_config => $self->config,
first_layer_flow => $first_layer_flow, first_layer_flow => $first_layer_flow,
flow => $self->support_material_flow, flow => $self->support_material_flow,
interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE),
); );
$support_material->generate($self);
} else { } else {
# New supports, C++ implementation. # New supports, C++ implementation.
return Slic3r::Print::SupportMaterial2->new($self); $self->_generate_support_material;
} }
} }
$self->set_step_done(STEP_SUPPORTMATERIAL);
}
# Idempotence of this method is guaranteed by the fact that we don't remove things from # 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,
); );

View file

@ -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;
if (! $synchronize) {
push @z,
# TODO: why we have this? # TODO: why we have this?
# Vojtech: To detect the bottom interface layers by finding a Z value in the $top_z. # Vojtech: To detect the bottom interface layers by finding a Z value in the $top_z.
@$top_z, @$top_z;
push @z,
# Top surfaces of the bottom interface layers. # Top surfaces of the bottom interface layers.
(map $_ + $contact_distance, @$top_z); (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,6 +429,11 @@ sub support_layers_z {
1..($self->object_config->raft_layers - 2); 1..($self->object_config->raft_layers - 2);
} }
if ($synchronize) {
@z = splice @z, $self->object_config->raft_layers;
# if ($self->object_config->raft_layers > scalar(@z));
push @z, map $_->print_z, @{$object->layers};
} else {
# create other layers (skip raft layers as they're already done and use thicker layers) # create other layers (skip raft layers as they're already done and use thicker layers)
for (my $i = $#z; $i >= $self->object_config->raft_layers; $i--) { for (my $i = $#z; $i >= $self->object_config->raft_layers; $i--) {
my $target_height = $support_material_height; my $target_height = $support_material_height;
@ -442,6 +453,7 @@ sub support_layers_z {
$i++; $i++;
} }
} }
}
# remove duplicates and make sure all 0.x values have the leading 0 # remove duplicates and make sure all 0.x values have the leading 0
{ {
@ -584,9 +596,8 @@ sub generate_base_layers {
$fillet_radius_scaled, $fillet_radius_scaled,
-$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 = ();
{ {

View file

@ -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

View file

@ -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,

View file

@ -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}) {
# Dynamic linking of boost libraries.
push @cflags, qw(-DBOOST_LOG_DYN_LINK);
if (! $mswin) { if (! $mswin) {
# Check without explicit lib path (works on Linux and OSX).
$have_boost = 1 $have_boost = 1
if check_lib( if check_lib(
lib => [ map "boost_${_}", @boost_libraries ], 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',

View file

@ -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

View file

@ -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!"

View file

@ -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

View file

@ -154,16 +154,34 @@ private:
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
}; };

View file

@ -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);
}
}
} }

View file

@ -65,6 +65,9 @@ class BoundingBox : public BoundingBoxBase<Point>
BoundingBox rotated(double angle, const Point &center) const; BoundingBox rotated(double angle, const Point &center) const;
void rotate(double angle) { (*this) = this->rotated(angle); } void rotate(double angle) { (*this) = this->rotated(angle); }
void rotate(double angle, const Point &center) { (*this) = this->rotated(angle, center); } void rotate(double angle, const Point &center) { (*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) {};

View file

@ -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,17 +203,19 @@ 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;
Polygons covered;
if (angle != -1) {
// Get anchors, convert them to Polygons and rotate them. // Get anchors, convert them to Polygons and rotate them.
Polygons anchors = to_polygons(this->_anchor_regions); Polygons anchors = to_polygons(this->_anchor_regions);
polygons_rotate(anchors, PI/2.0 - angle); polygons_rotate(anchors, PI/2.0 - angle);
Polygons covered;
for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly)
{ {
// Clone our expolygon and rotate it so that we work with vertical lines. // Clone our expolygon and rotate it so that we work with vertical lines.
@ -231,7 +233,7 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const
it->get_trapezoids2(&trapezoids, PI/2.0); it->get_trapezoids2(&trapezoids, PI/2.0);
for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) { for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
Lines supported = intersection(trapezoid->lines(), anchors); Lines supported = intersection_ln(trapezoid->lines(), anchors);
size_t n_supported = 0; size_t n_supported = 0;
// not nice, we need a more robust non-numeric check // not nice, we need a more robust non-numeric check
for (size_t i = 0; i < supported.size(); ++i) for (size_t i = 0; i < supported.size(); ++i)
@ -248,7 +250,7 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const
// Intersect trapezoids with actual bridge area to remove extra margins and append it to result. // Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
polygons_rotate(covered, -(PI/2.0 - angle)); polygons_rotate(covered, -(PI/2.0 - angle));
intersection(covered, to_polygons(this->expolygons), coverage); covered = intersection(covered, to_polygons(this->expolygons));
/* /*
if (0) { if (0) {
@ -266,13 +268,7 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const
} }
*/ */
} }
return covered;
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

View file

@ -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

View file

@ -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

View file

@ -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();
}
}; };
}; };

View file

@ -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 */
} }
@ -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,8 +1386,8 @@ 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;
} }
} }

View file

@ -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;

View file

@ -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

View file

@ -14,8 +14,16 @@ 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);
} }
}; };

View file

@ -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

View file

@ -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

View file

@ -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.;

View file

@ -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.

View file

@ -246,20 +246,19 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow()); 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);
ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
out.entities.push_back(&collection);
// Only concentric fills are not sorted. // Only concentric fills are not sorted.
collection.no_sort = f->no_sort(); eec->no_sort = f->no_sort();
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) { extrusion_entities_append_paths(
ExtrusionPath *path = new ExtrusionPath(role, flow.mm3_per_mm(), flow.width, flow.height); eec->entities, STDMOVE(polylines),
collection.entities.push_back(path); is_bridge ?
path->polyline.points.swap(it->points); erBridgeInfill :
} (surface.is_solid() ?
} ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) :
erInternalInfill),
flow.mm3_per_mm(), flow.width, flow.height);
} }
// add thin fill regions // add thin fill regions

View file

@ -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

View file

@ -45,8 +45,7 @@ Fill* Fill::new_from_type(const std::string &type)
Polylines Fill::fill_surface(const Surface *surface, const FillParams &params) Polylines Fill::fill_surface(const Surface *surface, const FillParams &params)
{ {
// 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)

View file

@ -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();

View file

@ -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());

View file

@ -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) {

View file

@ -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;

View file

@ -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;

View file

@ -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
} }
} }

View file

@ -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,
@ -23,7 +29,15 @@ 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.
// 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; bool bridge;
Flow(float _w, float _h, float _nd, bool _bridge = false) Flow(float _w, float _h, float _nd, bool _bridge = false)

View file

@ -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);
} }

View file

@ -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()
{
}
} }

View file

@ -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() {}
}; };

View file

@ -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));

View file

@ -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;
} }
}; };

View file

@ -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>

View file

@ -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 {

View file

@ -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();

View file

@ -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
{ {

View file

@ -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;

View file

@ -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

View file

@ -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);
} }
}; };

View file

@ -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);
} }
} }
} }

View file

@ -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 {
@ -79,6 +79,10 @@ public:
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
bool typed_slices; bool typed_slices;
@ -137,11 +141,25 @@ public:
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;
@ -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

View file

@ -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,8 +1316,10 @@ 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";

View file

@ -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);

View file

@ -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 &region = **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

View file

@ -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); }
}; };
} }

View 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

View 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_ */

View 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 &current_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

View 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 &current_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

View file

@ -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_extra_below;
coordf_t m_gap_xy; 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

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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,26 +431,47 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
At the end, we free the tables generated by analyze() as we don't 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());
{
boost::mutex lines_mutex;
parallelize<int>(
0,
this->mesh->stl.stats.number_of_facets-1,
boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z)
);
}
for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { // v_scaled_shared could be freed here
stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
// build loops
layers->resize(z.size());
parallelize<size_t>(
0,
lines.size()-1,
boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers)
);
}
void
TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex,
const std::vector<float> &z) const
{
const stl_facet &facet = this->mesh->stl.facet_start[facet_idx];
// find facet extents // find facet extents
float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z)); const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z));
float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z));
#ifdef SLIC3R_TRIANGLEMESH_DEBUG #ifdef SLIC3R_DEBUG
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, 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[0].x, facet.vertex[0].y, facet.vertex[0].z,
facet->vertex[1].x, facet->vertex[1].y, facet->vertex[1].z, facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z,
facet->vertex[2].x, facet->vertex[2].y, facet->vertex[2].z); facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z);
printf("z: min = %.2f, max = %.2f\n", min_z, max_z); printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
#endif #endif
@ -454,31 +479,18 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
std::vector<float>::const_iterator min_layer, max_layer; 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 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 max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
#ifdef SLIC3R_TRIANGLEMESH_DEBUG #ifdef SLIC3R_DEBUG
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin())); printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
#endif #endif
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) { for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
std::vector<float>::size_type layer_idx = it - z.begin(); 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]); this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex);
}
}
// v_scaled_shared could be freed here
// build loops
layers->resize(z.size());
for (std::vector<IntersectionLines>::iterator it = lines.begin(); it != lines.end(); ++it) {
size_t layer_idx = it - lines.begin();
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("Layer " PRINTF_ZU ":\n", layer_idx);
#endif
this->make_loops(*it, &(*layers)[layer_idx]);
} }
} }
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;
if (lines_mutex != NULL) {
boost::lock_guard<boost::mutex> l(*lines_mutex);
lines->push_back(line); 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;
if (lines_mutex != NULL) {
boost::lock_guard<boost::mutex> l(*lines_mutex);
lines->push_back(line); 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)

View file

@ -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;
}; };
} }

View 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_

View file

@ -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

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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 = [

View file

@ -30,6 +30,7 @@
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; %};
%{ %{
@ -69,6 +70,7 @@ 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; %};
%{ %{
@ -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; %};
}; };

View file

@ -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

View file

@ -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

View file

@ -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();
@ -107,10 +112,31 @@ _constant()
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; %};

View file

@ -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
%}

View file

@ -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

View file

@ -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: