Merge branch 'master' into dev

This commit is contained in:
bubnikv 2016-12-15 12:54:34 +01:00 committed by GitHub
commit 6400cec7ae
76 changed files with 3511 additions and 1897 deletions

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

@ -53,10 +53,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 +87,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 +139,11 @@ 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->reset_objects; $self->reset_objects;
EVT_PAINT($self, sub { EVT_PAINT($self, sub {
@ -178,6 +191,22 @@ sub new {
return $self; return $self;
} }
sub Destroy {
my ($self) = @_;
$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;
}
sub mouse_event { sub mouse_event {
my ($self, $e) = @_; my ($self, $e) = @_;
@ -192,9 +221,33 @@ sub mouse_event {
# 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);
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.
my ($cw, $ch) = $self->GetSizeWH;
my $bar_width = 70;
if ($e->GetX >= $cw - $bar_width) {
# Start editing the layer height.
$self->_layer_height_edited(1);
my $z = unscale($self->{print}->get_object($object_idx_selected)->size->z) * ($ch - $e->GetY - 1.) / ($ch - 1);
# print "Modifying height profile at $z\n";
# $self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile($z, $e->RightDown ? - 0.05 : 0.05, 2., 0);
$self->{print}->get_object($object_idx_selected)->generate_layer_height_texture(
$self->volumes->[$object_idx_selected]->layer_height_texture_data->ptr,
$self->{layer_preview_z_texture_height},
$self->{layer_preview_z_texture_width});
$self->Refresh;
}
}
}
# select volume in this 3D canvas if (! $self->_layer_height_edited) {
if ($self->enable_picking) { # 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 +278,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 +304,22 @@ 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) {
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.
my ($cw, $ch) = $self->GetSizeWH;
my $z = unscale($self->{print}->get_object($object_idx_selected)->size->z) * ($ch - $e->GetY - 1.) / ($ch - 1);
# print "Modifying height profile at $z\n";
my $strength = 0.005;
$self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile($z, $e->RightIsDown ? - $strength : $strength, 2., $e->ShiftDown ? 1 : 0);
$self->{print}->get_object($object_idx_selected)->generate_layer_height_texture(
$self->volumes->[$object_idx_selected]->layer_height_texture_data->ptr,
$self->{layer_preview_z_texture_height},
$self->{layer_preview_z_texture_width});
$self->Refresh;
}
} 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,6 +374,7 @@ 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);
} elsif ($e->Moving) { } elsif ($e->Moving) {
$self->_mouse_pos($pos); $self->_mouse_pos($pos);
$self->Refresh; $self->Refresh;
@ -721,6 +791,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 +847,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 +891,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 +925,7 @@ sub Render {
glFlush(); glFlush();
glFinish(); glFinish();
glEnable(GL_LIGHTING); glEnable(GL_LIGHTING);
glEnable(GL_MULTISAMPLE);
} }
# draw fixed background # draw fixed background
@ -948,6 +1045,8 @@ sub Render {
glDisable(GL_BLEND); glDisable(GL_BLEND);
} }
$self->draw_active_object_annotations;
glFlush(); glFlush();
$self->SwapBuffers(); $self->SwapBuffers();
@ -963,10 +1062,58 @@ 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');
die if ! defined($z_to_texture_row_id);
die if ! defined($z_texture_row_to_normalized_id);
die if ! defined($z_cursor_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);
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 +1164,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 +1206,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 +1327,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)))) + 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 +1440,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 +1463,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 +1508,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 +1517,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 +1535,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 +1569,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 +1588,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 +1938,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

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

@ -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 {
@ -863,7 +486,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 +516,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);
@ -299,9 +299,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
); );
} }
@ -584,9 +583,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.
} }

View File

@ -119,15 +119,19 @@ if (defined $ENV{BOOST_LIBRARYDIR}) {
my $have_boost = 0; my $have_boost = 0;
my @boost_libraries = qw(system thread log); # we need these my @boost_libraries = qw(system thread log); # we need these
# check without explicit lib path (works on Linux) if (!$ENV{SLIC3R_STATIC}) {
if (! $mswin) { # Dynamic linking of boost libraries.
push @cflags, qw(-DBOOST_LOG_DYN_LINK);
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,7 +142,6 @@ 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] =~ /\Q${lib_prefix}system\E([^.]*)\Q$lib_ext\E$/) {
# Suffix contains the version number, the build type etc. # Suffix contains the version number, the build type etc.
my $suffix = $1; my $suffix = $1;
@ -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

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) {
@ -265,14 +267,8 @@ 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,151 +14,197 @@ 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);

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)
@ -1222,6 +1222,114 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
return true; return true;
} }
Polygons EdgeGrid::Grid::contours_simplified() const
{
typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
// 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) {
bool left = cell_inside_or_crossing(r , c-1);
bool top = cell_inside_or_crossing(r-1, c );
bool current = cell_inside_or_crossing(r , c );
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());
coord_t downscale = 5;
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) ? downscale : -downscale;
p.x += (v.y > 0) ? downscale : -downscale;
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)
{ {

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

@ -293,37 +293,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();
@ -333,27 +333,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;
} }
@ -361,32 +361,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

@ -194,6 +194,26 @@ 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) 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()); dst.reserve(dst.size() + polylines.size());

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

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

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

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;

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

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

@ -608,20 +608,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,24 @@ 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();
private: private:
Print* _print; Print* _print;
@ -152,6 +169,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;

View File

@ -1,4 +1,5 @@
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
#include <boost/thread.hpp>
namespace Slic3r { namespace Slic3r {
@ -1290,8 +1291,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

@ -2,10 +2,23 @@
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
#include "SVG.hpp"
#include <boost/log/trivial.hpp>
#include <Shiny/Shiny.h> #include <Shiny/Shiny.h>
// #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)
@ -323,7 +336,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 +449,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 +458,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 +483,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 +513,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) {
@ -692,8 +710,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 +722,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
@ -754,9 +771,9 @@ PrintObject::discover_vertical_shells()
// 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);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells"); layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells");
@ -775,6 +792,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 +865,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 +921,326 @@ 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);
}
} // namespace Slic3r

View File

@ -0,0 +1,585 @@
#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_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;
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.
coordf_t print_z = first_layer_height + object_config.support_material_contact_distance.value;
if (params.raft_layers() == 1) {
params.contact_raft_layer_height = first_layer_height;
} else {
print_z +=
// Number of the base raft layers is decreased by the first layer, which has already been added to print_z.
coordf_t(params.base_raft_layers - 1) * params.base_raft_layer_height +
// Number of the interface raft layers is decreased by the contact layer.
coordf_t(params.interface_raft_layers - 1) * params.interface_raft_layer_height +
params.contact_raft_layer_height;
}
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,
int 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 0:
default:
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 1:
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;
}
// 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;
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 0:
default:
height += weight * layer_thickness_delta;
break;
case 1:
{
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;
}
}
// 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;
if (i < layer_height_profile.size()) {
if (profile_new[profile_new.size() - 2] + z_step < layer_height_profile[i]) {
profile_new.push_back(profile_new[profile_new.size() - 2] + z_step);
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);
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,112 @@
// 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;
// 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;
// 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 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);
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,
int 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_ */

View File

@ -5,11 +5,12 @@
#include "Print.hpp" #include "Print.hpp"
#include "SupportMaterial.hpp" #include "SupportMaterial.hpp"
#include "Fill/FillBase.hpp" #include "Fill/FillBase.hpp"
#include "EdgeGrid.hpp"
#include <cmath> #include <cmath>
#include <cassert>
#include <memory> #include <memory>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <unordered_set>
// #define SLIC3R_DEBUG // #define SLIC3R_DEBUG
@ -19,6 +20,8 @@
#include "SVG.hpp" #include "SVG.hpp"
#endif #endif
#include <cassert>
namespace Slic3r { namespace Slic3r {
// Increment used to reach MARGIN in steps to avoid trespassing thin objects // Increment used to reach MARGIN in steps to avoid trespassing thin objects
@ -241,16 +244,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
// Determine the bottom contact surfaces of the supports over the top surfaces of the object. // Determine the bottom contact surfaces of the supports over the top surfaces of the object.
// Depending on whether the support is soluble or not, the contact layer thickness is decided. // Depending on whether the support is soluble or not, the contact layer thickness is decided.
MyLayersPtr bottom_contacts = this->bottom_contact_layers(object, top_contacts, layer_storage); std::vector<Polygons> layer_support_areas;
MyLayersPtr bottom_contacts = this->bottom_contact_layers_and_layer_support_areas(
#ifdef SLIC3R_DEBUG object, top_contacts, layer_storage,
for (MyLayersPtr::const_iterator it = bottom_contacts.begin(); it != bottom_contacts.end(); ++ it) { layer_support_areas);
const MyLayer &layer = *(*it);
::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer.print_z), get_extents(layer.polygons));
Slic3r::ExPolygons expolys = union_ex(layer.polygons, false);
svg.draw(expolys);
}
#endif /* SLIC3R_DEBUG */
BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts"; BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts";
@ -271,7 +268,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers"; BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers";
// Fill in intermediate layers between the top / bottom support contact layers, trimmed by the object. // Fill in intermediate layers between the top / bottom support contact layers, trimmed by the object.
this->generate_base_layers(object, bottom_contacts, top_contacts, intermediate_layers); this->generate_base_layers(object, bottom_contacts, top_contacts, intermediate_layers, layer_support_areas);
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) { for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) {
@ -437,7 +434,8 @@ Polygons collect_slices_outer(const Layer &layer)
} }
// Find the top contact surfaces of the support or the raft. // Find the top contact surfaces of the support or the raft.
PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers(const PrintObject &object, MyLayerStorage &layer_storage) const PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers(
const PrintObject &object, MyLayerStorage &layer_storage) const
{ {
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
static int iRun = 0; static int iRun = 0;
@ -577,7 +575,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
for (Polylines::iterator it = overhang_perimeters.begin(); it != overhang_perimeters.end(); ++ it) for (Polylines::iterator it = overhang_perimeters.begin(); it != overhang_perimeters.end(); ++ it)
it->points[0].x += 1; it->points[0].x += 1;
diff(overhang_perimeters, lower_grown_slices, &overhang_perimeters); overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
// only consider straight overhangs // only consider straight overhangs
// only consider overhangs having endpoints inside layer's slices // only consider overhangs having endpoints inside layer's slices
@ -590,13 +588,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
if (it->is_straight()) { if (it->is_straight()) {
it->extend_start(fw); it->extend_start(fw);
it->extend_end(fw); it->extend_end(fw);
if (layer.slices.contains(it->first_point()) && layer.slices.contains(it->last_point())) { if (layer.slices.contains(it->first_point()) && layer.slices.contains(it->last_point()))
// Offset a polyline into a polygon. // Offset a polyline into a polygon.
Polylines tmp; tmp.push_back(*it); polygons_append(bridged_perimeters, offset(*it, 0.5f * w + 10.f));
Polygons out;
offset(tmp, &out, 0.5f * w + 10.f);
polygons_append(bridged_perimeters, out);
}
} }
} }
bridged_perimeters = union_(bridged_perimeters); bridged_perimeters = union_(bridged_perimeters);
@ -613,13 +607,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
Polygons unsupported_bridge_polygons; Polygons unsupported_bridge_polygons;
for (Polylines::const_iterator it = layerm.unsupported_bridge_edges.polylines.begin(); for (Polylines::const_iterator it = layerm.unsupported_bridge_edges.polylines.begin();
it != layerm.unsupported_bridge_edges.polylines.end(); ++ it) { it != layerm.unsupported_bridge_edges.polylines.end(); ++ it)
// Offset a polyline into a polygon. // Offset a polyline into a polygon.
Polylines tmp; tmp.push_back(*it); polygons_append(unsupported_bridge_polygons, offset(*it, scale_(SUPPORT_MATERIAL_MARGIN)));
Polygons out;
offset(tmp, &out, scale_(SUPPORT_MATERIAL_MARGIN));
polygons_append(unsupported_bridge_polygons, out);
}
polygons_append(diff_polygons, intersection(unsupported_bridge_polygons, bridges)); polygons_append(diff_polygons, intersection(unsupported_bridge_polygons, bridges));
} else { } else {
// just remove bridged areas // just remove bridged areas
@ -664,10 +654,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
offset( offset(
diff_polygons, diff_polygons,
SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS, SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS,
CLIPPER_OFFSET_SCALE,
ClipperLib::jtRound, ClipperLib::jtRound,
// round mitter limit // round mitter limit
scale_(0.05) * CLIPPER_OFFSET_SCALE), scale_(0.05)),
slices_margin); slices_margin);
} }
} }
@ -753,14 +742,188 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
return contact_out; return contact_out;
} }
PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers( #if 0
const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const typedef std::unordered_set<Point,PointHash> PointHashMap;
void fillet(Polygon &poly, PointHashMap &new_points_hash_map)
{ {
if (poly.points.size() < 3)
// an invalid contour will not be modified.
return;
// Flag describing a contour point.
std::vector<char> point_flag(std::vector<char>(poly.points.size(), 0));
// Does a point belong to new points?
for (size_t i = 0; i < poly.points.size(); ++ i)
if (new_points_hash_map.find(poly.points[i]) != new_points_hash_map.end())
// Mark the point as from the new contour.
point_flag[i] = 1;
// Mark the intersection points between the old and new contours.
size_t j = poly.points.size() - 1;
bool has_some = false;
for (size_t i = 0; i < poly.points.size(); j = i, ++ i)
if ((point_flag[i] ^ point_flag[j]) & 1) {
point_flag[(point_flag[i] & 1) ? j : i] |= 2;
has_some = true;
}
if (! has_some)
return;
#ifdef SLIC3R_DEBUG
static int iRun = 0;
++ iRun;
{
FILE *pfile = ::fopen(debug_out_path("fillet-in-%d.bin", iRun).c_str(), "wb");
size_t cnt = poly.points.size();
::fwrite(&cnt, 1, sizeof(cnt), pfile);
::fwrite(poly.points.data(), cnt, sizeof(Point), pfile);
cnt = new_points_hash_map.size();
::fwrite(&cnt, 1, sizeof(cnt), pfile);
for (PointHashMap::iterator it = new_points_hash_map.begin(); it != new_points_hash_map.end(); ++ it) {
const Point &pt = *it;
::fwrite(&pt, 1, sizeof(Point), pfile);
}
::fclose(pfile);
}
::Slic3r::SVG svg(debug_out_path("fillet-%d.svg", iRun), get_extents(poly));
svg.draw(poly, "black", scale_(0.05));
for (size_t i = 0; i < poly.points.size(); ++ i) {
const Point &pt1 = poly.points[i];
const Point &pt2 = poly.points[(i+1)%poly.points.size()];
if (new_points_hash_map.find(pt1) != new_points_hash_map.end())
svg.draw(Line(pt1, pt2), "red", scale_(0.035));
if (new_points_hash_map.find(pt1) != new_points_hash_map.end() &&
new_points_hash_map.find(pt2) != new_points_hash_map.end())
svg.draw(Line(pt1, pt2), "red", scale_(0.05));
}
#endif
// Mark a range of points around the intersection points.
const double rounding_range = scale_(1.5);
std::vector<Pointf> pts;
pts.reserve(poly.points.size());
for (int i = 0; i < int(poly.points.size()); ++ i) {
if (point_flag[i] & 2) {
point_flag[i] |= 4;
// Extend a filetting span left / right from i by an Euclidian distance of rounding_range.
double d = 0.f;
const Point *pt = &poly.points[i];
for (int j = 1; j < int(poly.points.size()); ++ j) {
int idx = (i + j) % poly.points.size();
const Point *pt2 = &poly.points[idx];
d += pt->distance_to(*pt2);
if (d > rounding_range)
break;
point_flag[idx] |= 4;
//pt = pt2;
}
for (int j = 1; j < int(poly.points.size()); ++ j) {
int idx = (i + int(poly.points.size()) - j) % poly.points.size();
const Point *pt2 = &poly.points[idx];
d += pt->distance_to(*pt2);
if (d > rounding_range)
break;
point_flag[idx] |= 4;
//pt = pt2;
}
}
pts.push_back(Pointf(poly.points[i].x, poly.points[i].y));
}
//FIXME avoid filetting over long edges. Insert new points into long edges at the ends of the filetting interval.
// Perform the filetting over the marked vertices.
std::vector<Pointf> pts2(pts);
double laplacian_weight = 0.5;
for (size_t i_round = 0; i_round < 5; ++ i_round) {
for (size_t i = 0; i < int(pts.size()); ++ i) {
if (point_flag[i] & 4) {
size_t prev = (i == 0) ? pts.size() - 1 : i - 1;
size_t next = (i + 1 == pts.size()) ? 0 : i + 1;
Pointf &p0 = pts[prev];
Pointf &p1 = pts[i];
Pointf &p2 = pts[next];
// Is the point reflex?
coordf_t c = cross(p1 - p0, p2 - p1);
if (c < 0)
// The point is reflex, perform Laplacian smoothing.
pts2[i] = (1. - laplacian_weight) * pts[i] + (0.5 * laplacian_weight) * (pts[prev] + pts[next]);
}
}
pts.swap(pts2);
}
// Mark vertices representing short edges for removal.
// Convert the filetted points back, remove points marked for removal.
j = 0;
for (size_t i = 0; i < poly.points.size(); ++ i) {
if (point_flag[i] & 8)
// Remove this point.
continue;
if (point_flag[i] & 4)
// Update the point coordinates.
poly.points[i] = Point(pts[i].x, pts[i].y);
if (j < i)
poly.points[j] = poly.points[i];
++ j;
}
if (j < poly.points.size())
poly.points.erase(poly.points.begin() + j, poly.points.end());
#ifdef SLIC3R_DEBUG
svg.draw_outline(poly, "blue", scale_(0.025));
#endif /* SLIC3R_DEBUG */
}
void fillet(Polygons &polygons, PointHashMap &new_points_hash_map)
{
for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
fillet(*it, new_points_hash_map);
}
void union_and_fillet(Polygons &polygons, size_t n_polygons_old)
{
if (n_polygons_old == polygons.size())
// No new polygons.
return;
// Fill in the new_points hash table with points of new contours.
PointHashMap new_points;
for (size_t i = n_polygons_old; i < polygons.size(); ++ i) {
const Polygon &poly = polygons[i];
for (size_t j = 0; j < poly.points.size(); ++ j)
new_points.insert(poly.points[j]);
}
// Merge the newly added regions. Don't use the safety offset, the offset has been added already.
polygons = union_(polygons, false);
// Fillet transition between the old and new points.
fillet(polygons, new_points);
}
#endif
// Collect
PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage,
std::vector<Polygons> &layer_support_areas) const
{
#ifdef SLIC3R_DEBUG
static int iRun = 0;
++ iRun;
#endif /* SLIC3R_DEBUG */
// Allocate empty surface areas, one per object layer.
layer_support_areas.assign(object.total_layer_count(), Polygons());
// find object top surfaces // find object top surfaces
// we'll use them to clip our support and detect where does it stick // we'll use them to clip our support and detect where does it stick
MyLayersPtr bottom_contacts; MyLayersPtr bottom_contacts;
if (! m_object_config->support_material_buildplate_only.value && ! top_contacts.empty())
if (! top_contacts.empty())
{ {
// There is some support to be built, if there are non-empty top surfaces detected.
// Sum of unsupported contact areas above the current layer.print_z. // Sum of unsupported contact areas above the current layer.print_z.
Polygons projection; Polygons projection;
// Last top contact layer visited when collecting the projection of contact areas. // Last top contact layer visited when collecting the projection of contact areas.
@ -768,32 +931,53 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
for (int layer_id = int(object.total_layer_count()) - 2; layer_id >= 0; -- layer_id) { for (int layer_id = int(object.total_layer_count()) - 2; layer_id >= 0; -- layer_id) {
BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id; BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id;
const Layer &layer = *object.get_layer(layer_id); const Layer &layer = *object.get_layer(layer_id);
Polygons top = collect_region_slices_by_type(layer, stTop); // Top surfaces of this layer, to be used to stop the surface volume from growing down.
if (top.empty()) Polygons top;
continue; if (! m_object_config->support_material_buildplate_only)
top = collect_region_slices_by_type(layer, stTop);
size_t projection_size_old = projection.size(); size_t projection_size_old = projection.size();
// Collect projections of all contact areas above or at the same level as this top surface. // Collect projections of all contact areas above or at the same level as this top surface.
for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) { for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) {
Polygons polygons_new;
// Contact surfaces are expanded away from the object, trimmed by the object. // Contact surfaces are expanded away from the object, trimmed by the object.
// Use a slight positive offset to overlap the touching regions. // Use a slight positive offset to overlap the touching regions.
polygons_append(projection, offset(top_contacts[contact_idx]->polygons, SCALED_EPSILON)); polygons_append(polygons_new, offset(top_contacts[contact_idx]->polygons, SCALED_EPSILON));
size_t size1 = polygons_new.size();
// These are the overhang surfaces. They are touching the object and they are not expanded away from the object. // These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
// Use a slight positive offset to overlap the touching regions. // Use a slight positive offset to overlap the touching regions.
polygons_append(projection, offset(*top_contacts[contact_idx]->aux_polygons, SCALED_EPSILON)); polygons_append(polygons_new, offset(*top_contacts[contact_idx]->aux_polygons, SCALED_EPSILON));
#if 0
union_and_fillet(polygons_new, size1);
#else
union_(polygons_new);
#endif
polygons_append(projection, std::move(polygons_new));
} }
if (projection.empty()) if (projection.empty())
continue; continue;
if (projection_size_old < projection.size()) { #if 0
// Merge the newly added regions. Don't use the safety offset, the offset has been added already. union_and_fillet(projection, projection_size_old);
projection = union_(projection, false); #else
union_(projection);
#endif
#ifdef SLIC3R_DEBUG
{
BoundingBox bbox = get_extents(projection);
bbox.merge(get_extents(top));
::Slic3r::SVG svg(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z), bbox);
svg.draw(union_ex(top, false), "blue", 0.5f);
svg.draw(union_ex(projection, true), "red", 0.5f);
svg.draw(layer.slices.expolygons, "green", 0.5f);
} }
#endif /* SLIC3R_DEBUG */
// Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any
// top surfaces above layer.print_z falls onto this top surface. // top surfaces above layer.print_z falls onto this top surface.
// touching are the contact surfaces supported exclusively by this top surfaaces. // touching are the contact surfaces supported exclusively by this top surfaaces.
// Don't use a safety offset as it has been applied during insertion of polygons. // Don't use a safety offset as it has been applied during insertion of polygons.
if (! top.empty()) {
Polygons touching = intersection(top, projection, false); Polygons touching = intersection(top, projection, false);
if (touching.empty()) if (! touching.empty()) {
continue;
// Allocate a new bottom contact layer. // Allocate a new bottom contact layer.
MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact); MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact);
bottom_contacts.push_back(&layer_new); bottom_contacts.push_back(&layer_new);
@ -812,12 +996,51 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
layer_new.bridging = ! m_soluble_interface; layer_new.bridging = ! m_soluble_interface;
//FIXME how much to inflate the top surface? //FIXME how much to inflate the top surface?
layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width())); layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()));
// Remove the areas that touched from the projection that will continue on next, lower, top surfaces. #ifdef SLIC3R_DEBUG
projection = diff(projection, touching); {
::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z), get_extents(layer_new.polygons));
Slic3r::ExPolygons expolys = union_ex(layer_new.polygons, false);
svg.draw(expolys);
} }
#endif /* SLIC3R_DEBUG */
} }
} // ! top.empty()
remove_sticks(projection);
remove_degenerate(projection);
// Create an EdgeGrid, initialize it with projection, initialize signed distance field.
Slic3r::EdgeGrid::Grid grid;
coord_t grid_resolution = scale_(1.5f);
BoundingBox bbox = get_extents(projection);
bbox.offset(20);
bbox.align_to_grid(grid_resolution);
grid.set_bbox(bbox);
grid.create(projection, grid_resolution);
grid.calculate_sdf();
// Extract a bounding contour from the grid.
Polygons projection_simplified = grid.contours_simplified();
#ifdef SLIC3R_DEBUG
{
BoundingBox bbox = get_extents(projection);
bbox.merge(get_extents(projection_simplified));
::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-simplified-%d-%d.svg", iRun, layer_id), bbox);
svg.draw(union_ex(projection, false), "blue", 0.5);
svg.draw(union_ex(projection_simplified, false), "red", 0.5);
}
#endif /* SLIC3R_DEBUG */
projection = std::move(projection_simplified);
// Remove the areas that touched from the projection that will continue on next, lower, top surfaces.
// projection = diff(projection, touching);
projection = diff(projection, to_polygons(layer.slices.expolygons), true);
layer_support_areas[layer_id] = projection;
}
std::reverse(bottom_contacts.begin(), bottom_contacts.end()); std::reverse(bottom_contacts.begin(), bottom_contacts.end());
} // ! top_contacts.empty()
return bottom_contacts; return bottom_contacts;
} }
@ -952,7 +1175,8 @@ void PrintObjectSupportMaterial::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
{ {
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
static int iRun = 0; static int iRun = 0;
@ -965,23 +1189,48 @@ void PrintObjectSupportMaterial::generate_base_layers(
// coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing); // coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
int idx_top_contact_above = int(top_contacts.size()) - 1; int idx_top_contact_above = int(top_contacts.size()) - 1;
int idx_bottom_contact_overlapping = int(bottom_contacts.size()) - 1; int idx_bottom_contact_overlapping = int(bottom_contacts.size()) - 1;
int idx_object_layer_above = int(object.total_layer_count()) - 1;
for (int idx_intermediate = int(intermediate_layers.size()) - 1; idx_intermediate >= 0; -- idx_intermediate) for (int idx_intermediate = int(intermediate_layers.size()) - 1; idx_intermediate >= 0; -- idx_intermediate)
{ {
BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - creating layer " <<
idx_intermediate << " of " << intermediate_layers.size();
MyLayer &layer_intermediate = *intermediate_layers[idx_intermediate]; MyLayer &layer_intermediate = *intermediate_layers[idx_intermediate];
// New polygons for layer_intermediate.
Polygons polygons_new;
// Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new. // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new.
while (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->bottom_z > layer_intermediate.print_z + EPSILON) while (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->bottom_z > layer_intermediate.print_z + EPSILON)
-- idx_top_contact_above; -- idx_top_contact_above;
if (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->print_z > layer_intermediate.print_z)
polygons_append(polygons_new, top_contacts[idx_top_contact_above]->polygons);
// New polygons for layer_intermediate.
Polygons polygons_new;
#if 0
// Add polygons projected from the intermediate layer above. // Add polygons projected from the intermediate layer above.
if (idx_intermediate + 1 < int(intermediate_layers.size())) if (idx_intermediate + 1 < int(intermediate_layers.size()))
polygons_append(polygons_new, intermediate_layers[idx_intermediate+1]->polygons); polygons_append(polygons_new, intermediate_layers[idx_intermediate+1]->polygons);
if (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->print_z > layer_intermediate.print_z) {
// Contact surfaces are expanded away from the object, trimmed by the object.
// Use a slight positive offset to overlap the touching regions.
Polygons polygons_new2;
polygons_append(polygons_new2, offset(top_contacts[idx_top_contact_above]->polygons, SCALED_EPSILON));
size_t size2 = polygons_new2.size();
// These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
// Use a slight positive offset to overlap the touching regions.
polygons_append(polygons_new2, offset(*top_contacts[idx_top_contact_above]->aux_polygons, SCALED_EPSILON));
union_and_fillet(polygons_new2, size2);
if (! polygons_new2.empty()) {
size_t polygons_size_old = polygons_new.size();
polygons_append(polygons_new, std::move(polygons_new2));
union_and_fillet(polygons_new, polygons_size_old);
}
}
#else
// Use the precomputed layer_support_areas.
while (idx_object_layer_above > 0 && object.get_layer(idx_object_layer_above - 1)->print_z > layer_intermediate.print_z - EPSILON)
-- idx_object_layer_above;
polygons_new = layer_support_areas[idx_object_layer_above];
#endif
// Polygons to trim polygons_new. // Polygons to trim polygons_new.
Polygons polygons_trimming; Polygons polygons_trimming;
@ -1004,7 +1253,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
-- idx_bottom_contact_overlapping; -- idx_bottom_contact_overlapping;
// Collect all the top_contact layer intersecting with this layer. // Collect all the top_contact layer intersecting with this layer.
for (int i = idx_bottom_contact_overlapping; i >= 0; -- i) { for (int i = idx_bottom_contact_overlapping; i >= 0; -- i) {
MyLayer &layer_bottom_overlapping = *bottom_contacts[idx_bottom_contact_overlapping]; MyLayer &layer_bottom_overlapping = *bottom_contacts[i];
if (layer_bottom_overlapping.print_z < layer_intermediate.print_z - layer_intermediate.height + EPSILON) if (layer_bottom_overlapping.print_z < layer_intermediate.print_z - layer_intermediate.height + EPSILON)
break; break;
polygons_append(polygons_trimming, layer_bottom_overlapping.polygons); polygons_append(polygons_trimming, layer_bottom_overlapping.polygons);
@ -1038,9 +1287,8 @@ void PrintObjectSupportMaterial::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,
false); // don't apply the safety offset. false); // don't apply the safety offset.
} }
@ -1063,6 +1311,9 @@ void PrintObjectSupportMaterial::generate_base_layers(
size_t idx_object_layer_overlapping = 0; size_t idx_object_layer_overlapping = 0;
// For all intermediate support layers: // For all intermediate support layers:
for (MyLayersPtr::iterator it_layer = intermediate_layers.begin(); it_layer != intermediate_layers.end(); ++ it_layer) { for (MyLayersPtr::iterator it_layer = intermediate_layers.begin(); it_layer != intermediate_layers.end(); ++ it_layer) {
BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - trimmming layer " <<
(it_layer - intermediate_layers.begin()) << " of " << intermediate_layers.size();
MyLayer &layer_intermediate = *(*it_layer); MyLayer &layer_intermediate = *(*it_layer);
if (layer_intermediate.polygons.empty()) if (layer_intermediate.polygons.empty())
// Empty support layer, nothing to trim. // Empty support layer, nothing to trim.
@ -1337,10 +1588,8 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
// Positions of the loop centers. // Positions of the loop centers.
Polygons circles; Polygons circles;
Polygons overhang_with_margin = offset(overhang_polygons, 0.5f * flow.scaled_width()); Polygons overhang_with_margin = offset(overhang_polygons, 0.5f * flow.scaled_width());
for (Polygons::const_iterator it_contact = top_contact_polygons.begin(); it_contact != top_contact_polygons.end(); ++ it_contact) { for (Polygons::const_iterator it_contact = top_contact_polygons.begin(); it_contact != top_contact_polygons.end(); ++ it_contact)
Polylines tmp; if (! intersection_pl(it_contact->split_at_first_point(), overhang_with_margin).empty()) {
tmp.push_back(it_contact->split_at_first_point());
if (! intersection(tmp, overhang_with_margin).empty()) {
external_loops.push_back(*it_contact); external_loops.push_back(*it_contact);
Points positions_new = it_contact->equally_spaced_points(circle_distance); Points positions_new = it_contact->equally_spaced_points(circle_distance);
for (Points::const_iterator it_center = positions_new.begin(); it_center != positions_new.end(); ++ it_center) { for (Points::const_iterator it_center = positions_new.begin(); it_center != positions_new.end(); ++ it_center) {
@ -1350,7 +1599,6 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
circle_new.points[i].translate(*it_center); circle_new.points[i].translate(*it_center);
} }
} }
}
// Apply a pattern to the loop. // Apply a pattern to the loop.
loops0 = diff(external_loops, circles); loops0 = diff(external_loops, circles);
} }
@ -1369,7 +1617,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
loop_lines.reserve(loop_polygons.size()); loop_lines.reserve(loop_polygons.size());
for (Polygons::const_iterator it = loop_polygons.begin(); it != loop_polygons.end(); ++ it) for (Polygons::const_iterator it = loop_polygons.begin(); it != loop_polygons.end(); ++ it)
loop_lines.push_back(it->split_at_first_point()); loop_lines.push_back(it->split_at_first_point());
loop_lines = intersection(loop_lines, offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN))); loop_lines = intersection_pl(loop_lines, offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN)));
} }
// add the contact infill area to the interface area // add the contact infill area to the interface area
@ -1377,9 +1625,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
// extrusions are left inside the circles; however it creates // extrusions are left inside the circles; however it creates
// a very large gap between loops and contact_infill_polygons, so maybe another // a very large gap between loops and contact_infill_polygons, so maybe another
// solution should be found to achieve both goals // solution should be found to achieve both goals
Polygons thick_loop_lines; top_contact_layer.layer->polygons = diff(top_contact_layer.layer->polygons, offset(loop_lines, float(circle_radius * 1.1)));
offset(loop_lines, &thick_loop_lines, float(circle_radius * 1.1));
top_contact_layer.layer->polygons = diff(top_contact_layer.layer->polygons, std::move(thick_loop_lines));
// Transform loops into ExtrusionPath objects. // Transform loops into ExtrusionPath objects.
extrusion_entities_append_paths( extrusion_entities_append_paths(
@ -1598,7 +1844,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// TODO: use brim ordering algorithm // TODO: use brim ordering algorithm
Polygons to_infill_polygons = to_polygons(to_infill); Polygons to_infill_polygons = to_polygons(to_infill);
// TODO: use offset2_ex() // TODO: use offset2_ex()
to_infill = offset_ex(to_infill_polygons, - flow.scaled_spacing()); to_infill = offset_ex(to_infill, - flow.scaled_spacing());
extrusion_entities_append_paths( extrusion_entities_append_paths(
support_layer.support_fills.entities, support_layer.support_fills.entities,
to_polylines(STDMOVE(to_infill_polygons)), to_polylines(STDMOVE(to_infill_polygons)),

View File

@ -163,7 +163,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;
@ -180,7 +182,8 @@ private:
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( Polygons generate_raft_base(
const PrintObject &object, const PrintObject &object,

View File

@ -196,19 +196,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

@ -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 < fminf(threads_count, 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
@ -66,30 +93,3 @@ confess_at(const char *file, int line, const char *func,
} }
#endif #endif
#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

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

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