Merge branch 'master' into dev
This commit is contained in:
commit
6400cec7ae
76 changed files with 3511 additions and 1897 deletions
|
@ -14,6 +14,7 @@ use Slic3r::GUI::ConfigWizard;
|
|||
use Slic3r::GUI::Controller;
|
||||
use Slic3r::GUI::Controller::ManualControlDialog;
|
||||
use Slic3r::GUI::Controller::PrinterPanel;
|
||||
use Slic3r::GUI::GLShader;
|
||||
use Slic3r::GUI::MainFrame;
|
||||
use Slic3r::GUI::Notifier;
|
||||
use Slic3r::GUI::Plater;
|
||||
|
|
|
@ -53,10 +53,15 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
|
|||
origin
|
||||
_mouse_pos
|
||||
_hover_volume_idx
|
||||
|
||||
_drag_volume_idx
|
||||
_drag_start_pos
|
||||
_drag_start_xy
|
||||
_dragged
|
||||
|
||||
layer_editing_enabled
|
||||
_layer_height_edited
|
||||
|
||||
_camera_type
|
||||
_camera_target
|
||||
_camera_distance
|
||||
|
@ -82,7 +87,10 @@ use constant VIEW_BOTTOM => [0.0,180.0];
|
|||
use constant VIEW_FRONT => [0.0,90.0];
|
||||
use constant VIEW_REAR => [180.0,90.0];
|
||||
|
||||
#use constant GIMBALL_LOCK_THETA_MAX => 150;
|
||||
use constant MANIPULATION_IDLE => 0;
|
||||
use constant MANIPULATION_DRAGGING => 1;
|
||||
use constant MANIPULATION_LAYER_HEIGHT => 2;
|
||||
|
||||
use constant GIMBALL_LOCK_THETA_MAX => 170;
|
||||
|
||||
# make OpenGL::Array thread-safe
|
||||
|
@ -130,6 +138,11 @@ sub new {
|
|||
# $self->_camera_type('perspective');
|
||||
$self->_camera_target(Slic3r::Pointf3->new(0,0,0));
|
||||
$self->_camera_distance(0.);
|
||||
|
||||
# Size of a layer height texture, used by a shader to color map the object print layers.
|
||||
$self->{layer_preview_z_texture_width} = 512;
|
||||
$self->{layer_preview_z_texture_height} = 512;
|
||||
$self->{layer_height_edit_band_width} = 2.;
|
||||
|
||||
$self->reset_objects;
|
||||
|
||||
|
@ -178,6 +191,22 @@ sub new {
|
|||
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 {
|
||||
my ($self, $e) = @_;
|
||||
|
||||
|
@ -192,40 +221,65 @@ sub mouse_event {
|
|||
# If user pressed left or right button we first check whether this happened
|
||||
# on a volume or not.
|
||||
my $volume_idx = $self->_hover_volume_idx // -1;
|
||||
|
||||
# select volume in this 3D canvas
|
||||
if ($self->enable_picking) {
|
||||
$self->deselect_volumes;
|
||||
$self->select_volume($volume_idx);
|
||||
|
||||
if ($volume_idx != -1) {
|
||||
my $group_id = $self->volumes->[$volume_idx]->select_group_id;
|
||||
my @volumes;
|
||||
if ($group_id != -1) {
|
||||
$self->select_volume($_)
|
||||
for grep $self->volumes->[$_]->select_group_id == $group_id,
|
||||
0..$#{$self->volumes};
|
||||
$self->_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;
|
||||
}
|
||||
}
|
||||
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
# propagate event through callback
|
||||
$self->on_select->($volume_idx)
|
||||
if $self->on_select;
|
||||
|
||||
if ($volume_idx != -1) {
|
||||
if ($e->LeftDown && $self->enable_moving) {
|
||||
$self->_drag_volume_idx($volume_idx);
|
||||
$self->_drag_start_pos($self->mouse_to_3d(@$pos));
|
||||
} elsif ($e->RightDown) {
|
||||
# if right clicking on volume, propagate event through callback
|
||||
$self->on_right_click->($e->GetPosition)
|
||||
if $self->on_right_click;
|
||||
|
||||
if (! $self->_layer_height_edited) {
|
||||
# Select volume in this 3D canvas.
|
||||
# Don't deselect a volume if layer editing is enabled. We want the object to stay selected
|
||||
# during the scene manipulation.
|
||||
if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) {
|
||||
$self->deselect_volumes;
|
||||
$self->select_volume($volume_idx);
|
||||
|
||||
if ($volume_idx != -1) {
|
||||
my $group_id = $self->volumes->[$volume_idx]->select_group_id;
|
||||
my @volumes;
|
||||
if ($group_id != -1) {
|
||||
$self->select_volume($_)
|
||||
for grep $self->volumes->[$_]->select_group_id == $group_id,
|
||||
0..$#{$self->volumes};
|
||||
}
|
||||
}
|
||||
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
# propagate event through callback
|
||||
$self->on_select->($volume_idx)
|
||||
if $self->on_select;
|
||||
|
||||
if ($volume_idx != -1) {
|
||||
if ($e->LeftDown && $self->enable_moving) {
|
||||
$self->_drag_volume_idx($volume_idx);
|
||||
$self->_drag_start_pos($self->mouse_to_3d(@$pos));
|
||||
} elsif ($e->RightDown) {
|
||||
# if right clicking on volume, propagate event through callback
|
||||
$self->on_right_click->($e->GetPosition)
|
||||
if $self->on_right_click;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elsif ($e->Dragging && $e->LeftIsDown && defined($self->_drag_volume_idx)) {
|
||||
} elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) {
|
||||
# get new position at the same Z of the initial click point
|
||||
my $mouse_ray = $self->mouse_ray($e->GetX, $e->GetY);
|
||||
my $cur_pos = $mouse_ray->intersect_plane($self->_drag_start_pos->z);
|
||||
|
@ -250,7 +304,22 @@ sub mouse_event {
|
|||
$self->_dragged(1);
|
||||
$self->Refresh;
|
||||
} 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 (defined $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_xy(undef);
|
||||
$self->_dragged(undef);
|
||||
$self->_layer_height_edited(undef);
|
||||
} elsif ($e->Moving) {
|
||||
$self->_mouse_pos($pos);
|
||||
$self->Refresh;
|
||||
|
@ -720,7 +790,22 @@ sub InitGL {
|
|||
return if $self->init;
|
||||
return unless $self->GetContext;
|
||||
$self->init(1);
|
||||
|
||||
|
||||
my $shader;
|
||||
$shader = $self->{shader} = new Slic3r::GUI::GLShader
|
||||
if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1);
|
||||
if ($self->{shader}) {
|
||||
my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader);
|
||||
print $info if $info;
|
||||
|
||||
($self->{layer_preview_z_texture_id}) = glGenTextures_p(1);
|
||||
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glColor3f(1, 0, 0);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
@ -761,7 +846,14 @@ sub InitGL {
|
|||
glEnable(GL_COLOR_MATERIAL);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
}
|
||||
|
||||
|
||||
sub DestroyGL {
|
||||
my $self = shift;
|
||||
if ($self->init && $self->GetContext) {
|
||||
delete $self->{shader};
|
||||
}
|
||||
}
|
||||
|
||||
sub Render {
|
||||
my ($self, $dc) = @_;
|
||||
|
||||
|
@ -798,11 +890,15 @@ sub Render {
|
|||
glLightfv_p(GL_LIGHT0, GL_POSITION, -0.5, -0.5, 1, 0);
|
||||
glLightfv_p(GL_LIGHT0, GL_SPECULAR, 0.2, 0.2, 0.2, 1);
|
||||
glLightfv_p(GL_LIGHT0, GL_DIFFUSE, 0.5, 0.5, 0.5, 1);
|
||||
|
||||
# Head light
|
||||
glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0);
|
||||
|
||||
if ($self->enable_picking) {
|
||||
# Render the object for picking.
|
||||
# FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing.
|
||||
# Better to use software ray-casting on a bounding-box hierarchy.
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
glDisable(GL_LIGHTING);
|
||||
$self->draw_volumes(1);
|
||||
glFlush();
|
||||
|
@ -829,6 +925,7 @@ sub Render {
|
|||
glFlush();
|
||||
glFinish();
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
}
|
||||
|
||||
# draw fixed background
|
||||
|
@ -928,7 +1025,7 @@ sub Render {
|
|||
|
||||
# draw objects
|
||||
$self->draw_volumes;
|
||||
|
||||
|
||||
# draw cutting plane
|
||||
if (defined $self->cutting_plane_z) {
|
||||
my $plane_z = $self->cutting_plane_z;
|
||||
|
@ -947,6 +1044,8 @@ sub Render {
|
|||
glEnable(GL_CULL_FACE);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
$self->draw_active_object_annotations;
|
||||
|
||||
glFlush();
|
||||
|
||||
|
@ -963,10 +1062,58 @@ sub draw_volumes {
|
|||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
# The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),
|
||||
# where x, y is the window size divided by $self->_zoom.
|
||||
my ($cw, $ch) = $self->GetSizeWH;
|
||||
my $bar_width = 70;
|
||||
my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom));
|
||||
my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom));
|
||||
my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition());
|
||||
my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. :
|
||||
($ch - $mouse_pos->y - 1.) / ($ch - 1);
|
||||
|
||||
foreach my $volume_idx (0..$#{$self->volumes}) {
|
||||
my $volume = $self->volumes->[$volume_idx];
|
||||
|
||||
if ($fakecolor) {
|
||||
|
||||
my $shader_active = 0;
|
||||
if ($self->layer_editing_enabled && ! $fakecolor && $volume->selected && $self->{shader} && $volume->{layer_height_texture_data} && $volume->{layer_height_texture_cells}) {
|
||||
$self->{shader}->Enable;
|
||||
my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row');
|
||||
my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized');
|
||||
my $z_cursor_id = $self->{shader}->Map('z_cursor');
|
||||
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.
|
||||
my $r = ($volume_idx & 0x000000FF) >> 0;
|
||||
my $g = ($volume_idx & 0x0000FF00) >> 8;
|
||||
|
@ -1017,16 +1164,39 @@ sub draw_volumes {
|
|||
if ($qverts_begin < $qverts_end) {
|
||||
glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr);
|
||||
glNormalPointer_c(GL_FLOAT, 0, $volume->qverts->norms_ptr);
|
||||
glDrawArrays(GL_QUADS, $qverts_begin / 3, ($qverts_end-$qverts_begin) / 3);
|
||||
$qverts_begin /= 3;
|
||||
$qverts_end /= 3;
|
||||
my $nvertices = $qverts_end-$qverts_begin;
|
||||
while ($nvertices > 0) {
|
||||
my $nvertices_this = ($nvertices > 4096) ? 4096 : $nvertices;
|
||||
glDrawArrays(GL_QUADS, $qverts_begin, $nvertices_this);
|
||||
$qverts_begin += $nvertices_this;
|
||||
$nvertices -= $nvertices_this;
|
||||
}
|
||||
}
|
||||
|
||||
if ($tverts_begin < $tverts_end) {
|
||||
glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr);
|
||||
glNormalPointer_c(GL_FLOAT, 0, $volume->tverts->norms_ptr);
|
||||
glDrawArrays(GL_TRIANGLES, $tverts_begin / 3, ($tverts_end-$tverts_begin) / 3);
|
||||
$tverts_begin /= 3;
|
||||
$tverts_end /= 3;
|
||||
my $nvertices = $tverts_end-$tverts_begin;
|
||||
while ($nvertices > 0) {
|
||||
my $nvertices_this = ($nvertices > 4095) ? 4095 : $nvertices;
|
||||
glDrawArrays(GL_TRIANGLES, $tverts_begin, $nvertices_this);
|
||||
$tverts_begin += $nvertices_this;
|
||||
$nvertices -= $nvertices_this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
glVertexPointer_c(3, GL_FLOAT, 0, 0);
|
||||
glNormalPointer_c(GL_FLOAT, 0, 0);
|
||||
glPopMatrix();
|
||||
|
||||
if ($shader_active) {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
$self->{shader}->Disable;
|
||||
}
|
||||
}
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
glDisable(GL_BLEND);
|
||||
|
@ -1036,10 +1206,98 @@ sub draw_volumes {
|
|||
glColor3f(0, 0, 0);
|
||||
glVertexPointer_p(3, $self->cut_lines_vertices);
|
||||
glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3);
|
||||
glVertexPointer_c(3, GL_FLOAT, 0, 0);
|
||||
}
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
sub draw_active_object_annotations {
|
||||
# $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking.
|
||||
my ($self) = @_;
|
||||
|
||||
return if (! $self->{shader} || ! $self->layer_editing_enabled);
|
||||
|
||||
my $volume;
|
||||
foreach my $volume_idx (0..$#{$self->volumes}) {
|
||||
my $v = $self->volumes->[$volume_idx];
|
||||
if ($v->selected && $v->{layer_height_texture_data} && $v->{layer_height_texture_cells}) {
|
||||
$volume = $v;
|
||||
last;
|
||||
}
|
||||
}
|
||||
return if (! $volume);
|
||||
|
||||
# The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),
|
||||
# where x, y is the window size divided by $self->_zoom.
|
||||
my ($cw, $ch) = $self->GetSizeWH;
|
||||
my $bar_width = 70;
|
||||
my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom));
|
||||
my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom));
|
||||
my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition());
|
||||
my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. :
|
||||
($ch - $mouse_pos->y - 1.) / ($ch - 1);
|
||||
|
||||
$self->{shader}->Enable;
|
||||
my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row');
|
||||
my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized');
|
||||
my $z_cursor_id = $self->{shader}->Map('z_cursor');
|
||||
my $ncells = $volume->{layer_height_texture_cells};
|
||||
my $z_max = $volume->{bounding_box}->z_max;
|
||||
glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max));
|
||||
glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height});
|
||||
glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative);
|
||||
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
|
||||
glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr);
|
||||
glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4));
|
||||
|
||||
# Render the color bar.
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
# The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),
|
||||
# where x, y is the window size divided by $self->_zoom.
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
# Paint the overlay.
|
||||
glBegin(GL_QUADS);
|
||||
glVertex3f($bar_left, $bar_bottom, 0);
|
||||
glVertex3f($bar_right, $bar_bottom, 0);
|
||||
glVertex3f($bar_right, $bar_top, $volume->{bounding_box}->z_max);
|
||||
glVertex3f($bar_left, $bar_top, $volume->{bounding_box}->z_max);
|
||||
glEnd();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
$self->{shader}->Disable;
|
||||
|
||||
# Paint the graph.
|
||||
my $object_idx = int($volume->select_group_id / 1000000);
|
||||
my $print_object = $self->{print}->get_object($object_idx);
|
||||
my $max_z = unscale($print_object->size->z);
|
||||
my $profile = $print_object->layer_height_profile;
|
||||
my $layer_height = $print_object->config->get('layer_height');
|
||||
# Baseline
|
||||
glColor3f(0., 0., 0.);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45, $bar_bottom);
|
||||
glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45, $bar_top);
|
||||
glEnd();
|
||||
# Curve
|
||||
glColor3f(0., 0., 1.);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for (my $i = 0; $i < int(@{$profile}); $i += 2) {
|
||||
my $z = $profile->[$i];
|
||||
my $h = $profile->[$i+1];
|
||||
glVertex3f($bar_left + $h * ($bar_right - $bar_left) / 0.45, $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z);
|
||||
}
|
||||
glEnd();
|
||||
# Revert the matrices.
|
||||
glPopMatrix();
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
sub _report_opengl_state
|
||||
{
|
||||
my ($self, $comment) = @_;
|
||||
|
@ -1069,6 +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.
|
||||
package Slic3r::GUI::3DScene::Volume;
|
||||
use Moo;
|
||||
|
@ -1076,6 +1440,8 @@ use Moo;
|
|||
has 'bounding_box' => (is => 'ro', required => 1);
|
||||
has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) });
|
||||
has 'color' => (is => 'ro', required => 1);
|
||||
# An ID containing the object ID, volume ID and instance ID.
|
||||
has 'composite_id' => (is => 'rw', default => sub { -1 });
|
||||
# An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance.
|
||||
has 'select_group_id' => (is => 'rw', default => sub { -1 });
|
||||
# An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance.
|
||||
|
@ -1097,6 +1463,26 @@ has 'tverts' => (is => 'rw');
|
|||
# The offsets stores tripples of (z_top, qverts_idx, tverts_idx) in a linear array.
|
||||
has 'offsets' => (is => 'rw');
|
||||
|
||||
# RGBA texture along the Z axis of an object, to visualize layers by stripes colored by their height.
|
||||
has 'layer_height_texture_data' => (is => 'rw');
|
||||
# Number of texture cells.
|
||||
has 'layer_height_texture_cells' => (is => 'rw');
|
||||
|
||||
sub object_idx {
|
||||
my ($self) = @_;
|
||||
return $self->composite_id / 1000000;
|
||||
}
|
||||
|
||||
sub volume_idx {
|
||||
my ($self) = @_;
|
||||
return ($self->composite_id / 1000) % 1000;
|
||||
}
|
||||
|
||||
sub instance_idx {
|
||||
my ($self) = @_;
|
||||
return $self->composite_id % 1000;
|
||||
}
|
||||
|
||||
sub transformed_bounding_box {
|
||||
my ($self) = @_;
|
||||
|
||||
|
@ -1122,8 +1508,6 @@ __PACKAGE__->mk_accessors(qw(
|
|||
color_by
|
||||
select_by
|
||||
drag_by
|
||||
volumes_by_object
|
||||
_objects_by_volumes
|
||||
));
|
||||
|
||||
sub new {
|
||||
|
@ -1133,14 +1517,12 @@ sub new {
|
|||
$self->color_by('volume'); # object | volume
|
||||
$self->select_by('object'); # object | volume | instance
|
||||
$self->drag_by('instance'); # object | instance
|
||||
$self->volumes_by_object({}); # obj_idx => [ volume_idx, volume_idx ... ]
|
||||
$self->_objects_by_volumes({}); # volume_idx => [ obj_idx, instance_idx ]
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub load_object {
|
||||
my ($self, $model, $obj_idx, $instance_idxs) = @_;
|
||||
my ($self, $model, $print, $obj_idx, $instance_idxs) = @_;
|
||||
|
||||
my $model_object;
|
||||
if ($model->isa('Slic3r::Model::Object')) {
|
||||
|
@ -1152,6 +1534,19 @@ sub load_object {
|
|||
}
|
||||
|
||||
$instance_idxs ||= [0..$#{$model_object->instances}];
|
||||
|
||||
# Object will have a single common layer height texture for all volumes.
|
||||
my $layer_height_texture_data;
|
||||
my $layer_height_texture_cells;
|
||||
if ($print && $obj_idx < $print->object_count) {
|
||||
# Generate the layer height texture. Allocate data for the 0th and 1st mipmap levels.
|
||||
$layer_height_texture_data = OpenGL::Array->new($self->{layer_preview_z_texture_width}*$self->{layer_preview_z_texture_height}*5, GL_UNSIGNED_BYTE);
|
||||
# $print->get_object($obj_idx)->update_layer_height_profile_from_ranges();
|
||||
$layer_height_texture_cells = $print->get_object($obj_idx)->generate_layer_height_texture(
|
||||
$layer_height_texture_data->ptr,
|
||||
$self->{layer_preview_z_texture_height},
|
||||
$self->{layer_preview_z_texture_width});
|
||||
}
|
||||
|
||||
my @volumes_idx = ();
|
||||
foreach my $volume_idx (0..$#{$model_object->volumes}) {
|
||||
|
@ -1174,16 +1569,18 @@ sub load_object {
|
|||
# not correspond to the color of the filament.
|
||||
my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ];
|
||||
$color->[3] = $volume->modifier ? 0.5 : 1;
|
||||
print "Reloading object $volume_idx, $instance_idx\n";
|
||||
push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new(
|
||||
bounding_box => $mesh->bounding_box,
|
||||
color => $color,
|
||||
);
|
||||
$v->composite_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx);
|
||||
if ($self->select_by eq 'object') {
|
||||
$v->select_group_id($obj_idx*1000000);
|
||||
} elsif ($self->select_by eq 'volume') {
|
||||
$v->select_group_id($obj_idx*1000000 + $volume_idx*1000);
|
||||
} elsif ($self->select_by eq 'instance') {
|
||||
$v->select_group_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx);
|
||||
$v->select_group_id($v->composite_id);
|
||||
}
|
||||
if ($self->drag_by eq 'object') {
|
||||
$v->drag_group_id($obj_idx*1000);
|
||||
|
@ -1191,15 +1588,18 @@ sub load_object {
|
|||
$v->drag_group_id($obj_idx*1000 + $instance_idx);
|
||||
}
|
||||
push @volumes_idx, my $scene_volume_idx = $#{$self->volumes};
|
||||
$self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $volume_idx, $instance_idx ];
|
||||
|
||||
my $verts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
||||
$verts->load_mesh($mesh);
|
||||
$v->tverts($verts);
|
||||
|
||||
if (! $volume->modifier) {
|
||||
$v->layer_height_texture_data($layer_height_texture_data);
|
||||
$v->layer_height_texture_cells($layer_height_texture_cells);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$self->volumes_by_object->{$obj_idx} = [@volumes_idx];
|
||||
return @volumes_idx;
|
||||
}
|
||||
|
||||
|
@ -1538,19 +1938,4 @@ sub _extrusionentity_to_verts {
|
|||
$tverts);
|
||||
}
|
||||
|
||||
sub object_idx {
|
||||
my ($self, $volume_idx) = @_;
|
||||
return $self->_objects_by_volumes->{$volume_idx}[0];
|
||||
}
|
||||
|
||||
sub volume_idx {
|
||||
my ($self, $volume_idx) = @_;
|
||||
return $self->_objects_by_volumes->{$volume_idx}[1];
|
||||
}
|
||||
|
||||
sub instance_idx {
|
||||
my ($self, $volume_idx) = @_;
|
||||
return $self->_objects_by_volumes->{$volume_idx}[2];
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
184
lib/Slic3r/GUI/GLShader.pm
Normal file
184
lib/Slic3r/GUI/GLShader.pm
Normal file
|
@ -0,0 +1,184 @@
|
|||
############################################################
|
||||
#
|
||||
# Stripped down from the Perl OpenGL::Shader package by Vojtech Bubnik
|
||||
# to only support the GLSL shaders. The original source was not maintained
|
||||
# and did not install properly through the CPAN archive, and it was unnecessary
|
||||
# complex.
|
||||
#
|
||||
# Original copyright:
|
||||
#
|
||||
# Copyright 2007 Graphcomp - ALL RIGHTS RESERVED
|
||||
# Author: Bob "grafman" Free - grafman@graphcomp.com
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the same terms as Perl itself.
|
||||
#
|
||||
############################################################
|
||||
|
||||
package Slic3r::GUI::GLShader;
|
||||
use OpenGL(':all');
|
||||
|
||||
# Avoid cloning this class by the worker threads.
|
||||
sub CLONE_SKIP { 1 }
|
||||
|
||||
# Shader constructor
|
||||
sub new
|
||||
{
|
||||
# Check for required OpenGL extensions
|
||||
return undef if (OpenGL::glpCheckExtension('GL_ARB_shader_objects'));
|
||||
return undef if (OpenGL::glpCheckExtension('GL_ARB_fragment_shader'));
|
||||
return undef if (OpenGL::glpCheckExtension('GL_ARB_vertex_shader'));
|
||||
return undef if (OpenGL::glpCheckExtension('GL_ARB_shading_language_100'));
|
||||
# my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||
# my $glsl_version_ARB = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB );
|
||||
# print "GLSL version: $glsl_version, ARB: $glsl_version_ARB\n";
|
||||
|
||||
my $this = shift;
|
||||
my $class = ref($this) || $this;
|
||||
my($type) = @_;
|
||||
my $self = {type => uc($type)};
|
||||
bless($self,$class);
|
||||
|
||||
# Get GL_SHADING_LANGUAGE_VERSION_ARB
|
||||
my $shader_ver = glGetString(0x8B8C);
|
||||
$shader_ver =~ m|([\d\.]+)|;
|
||||
$self->{version} = $1 || '0';
|
||||
print
|
||||
return $self;
|
||||
}
|
||||
|
||||
# Shader destructor
|
||||
# Must be disabled first
|
||||
sub DESTROY
|
||||
{
|
||||
my($self) = @_;
|
||||
|
||||
if ($self->{program})
|
||||
{
|
||||
glDetachObjectARB($self->{program},$self->{fragment_id}) if ($self->{fragment_id});
|
||||
glDetachObjectARB($self->{program},$self->{vertex_id}) if ($self->{vertex_id});
|
||||
glDeleteProgramsARB_p($self->{program});
|
||||
}
|
||||
|
||||
glDeleteProgramsARB_p($self->{fragment_id}) if ($self->{fragment_id});
|
||||
glDeleteProgramsARB_p($self->{vertex_id}) if ($self->{vertex_id});
|
||||
}
|
||||
|
||||
|
||||
# Load shader strings
|
||||
sub Load
|
||||
{
|
||||
my($self,$fragment,$vertex) = @_;
|
||||
|
||||
# Load fragment code
|
||||
if ($fragment)
|
||||
{
|
||||
$self->{fragment_id} = glCreateShaderObjectARB(GL_FRAGMENT_SHADER);
|
||||
return undef if (!$self->{fragment_id});
|
||||
|
||||
glShaderSourceARB_p($self->{fragment_id}, $fragment);
|
||||
#my $frag = glGetShaderSourceARB_p($self->{fragment_id});
|
||||
#print STDERR "Loaded fragment:\n$frag\n";
|
||||
|
||||
glCompileShaderARB($self->{fragment_id});
|
||||
my $stat = glGetInfoLogARB_p($self->{fragment_id});
|
||||
return "Fragment shader: $stat" if ($stat);
|
||||
}
|
||||
|
||||
# Load vertex code
|
||||
if ($vertex)
|
||||
{
|
||||
$self->{vertex_id} = glCreateShaderObjectARB(GL_VERTEX_SHADER);
|
||||
return undef if (!$self->{vertex_id});
|
||||
|
||||
glShaderSourceARB_p($self->{vertex_id}, $vertex);
|
||||
#my $vert = glGetShaderSourceARB_p($self->{vertex_id});
|
||||
#print STDERR "Loaded vertex:\n$vert\n";
|
||||
|
||||
glCompileShaderARB($self->{vertex_id});
|
||||
$stat = glGetInfoLogARB_p($self->{vertex_id});
|
||||
return "Vertex shader: $stat" if ($stat);
|
||||
}
|
||||
|
||||
|
||||
# Link shaders
|
||||
my $sp = glCreateProgramObjectARB();
|
||||
glAttachObjectARB($sp, $self->{fragment_id}) if ($fragment);
|
||||
glAttachObjectARB($sp, $self->{vertex_id}) if ($vertex);
|
||||
glLinkProgramARB($sp);
|
||||
my $linked = glGetObjectParameterivARB_p($sp, GL_OBJECT_LINK_STATUS_ARB);
|
||||
if (!$linked)
|
||||
{
|
||||
$stat = glGetInfoLogARB_p($sp);
|
||||
#print STDERR "Load shader: $stat\n";
|
||||
return "Link shader: $stat" if ($stat);
|
||||
return 'Unable to link shader';
|
||||
}
|
||||
|
||||
$self->{program} = $sp;
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
# Enable shader
|
||||
sub Enable
|
||||
{
|
||||
my($self) = @_;
|
||||
glUseProgramObjectARB($self->{program}) if ($self->{program});
|
||||
}
|
||||
|
||||
# Disable shader
|
||||
sub Disable
|
||||
{
|
||||
my($self) = @_;
|
||||
glUseProgramObjectARB(0) if ($self->{program});
|
||||
}
|
||||
|
||||
# Return shader vertex attribute ID
|
||||
sub MapAttr
|
||||
{
|
||||
my($self,$attr) = @_;
|
||||
return undef if (!$self->{program});
|
||||
my $id = glGetAttribLocationARB_p($self->{program},$attr);
|
||||
return undef if ($id < 0);
|
||||
return $id;
|
||||
}
|
||||
|
||||
# Return shader uniform variable ID
|
||||
sub Map
|
||||
{
|
||||
my($self,$var) = @_;
|
||||
return undef if (!$self->{program});
|
||||
my $id = glGetUniformLocationARB_p($self->{program},$var);
|
||||
return undef if ($id < 0);
|
||||
return $id;
|
||||
}
|
||||
|
||||
# Set shader vector
|
||||
sub SetVector
|
||||
{
|
||||
my($self,$var,@values) = @_;
|
||||
|
||||
my $id = $self->Map($var);
|
||||
return 'Unable to map $var' if (!defined($id));
|
||||
|
||||
my $count = scalar(@values);
|
||||
eval('glUniform'.$count.'fARB($id,@values)');
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
# Set shader 4x4 matrix
|
||||
sub SetMatrix
|
||||
{
|
||||
my($self,$var,$oga) = @_;
|
||||
|
||||
my $id = $self->Map($var);
|
||||
return 'Unable to map $var' if (!defined($id));
|
||||
|
||||
glUniformMatrix4fvARB_c($id,1,0,$oga->ptr());
|
||||
return '';
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
|
@ -42,6 +42,11 @@ sub new {
|
|||
$self->_init_tabpanel;
|
||||
$self->_init_menubar;
|
||||
|
||||
# set default tooltip timer in msec
|
||||
# SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values
|
||||
# (SetAutoPop is not available on GTK.)
|
||||
eval { Wx::ToolTip::SetAutoPop(32767) };
|
||||
|
||||
# initialize status bar
|
||||
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1);
|
||||
$self->{statusbar}->SetStatusText("Version $Slic3r::VERSION - Remember to check for updates at http://github.com/prusa3d/slic3r/releases");
|
||||
|
@ -292,7 +297,7 @@ sub _init_menubar {
|
|||
# View menu
|
||||
if (!$self->{no_plater}) {
|
||||
$self->{viewMenu} = Wx::Menu->new;
|
||||
$self->_append_menu_item($self->{viewMenu}, "Default", 'Default View', sub { $self->select_view('default'); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Iso" , 'Iso View' , sub { $self->select_view('iso' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Top" , 'Top View' , sub { $self->select_view('top' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Bottom" , 'Bottom View' , sub { $self->select_view('bottom' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Front" , 'Front View' , sub { $self->select_view('front' ); });
|
||||
|
|
|
@ -8,10 +8,11 @@ use utf8;
|
|||
use File::Basename qw(basename dirname);
|
||||
use List::Util qw(sum first max);
|
||||
use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad);
|
||||
use LWP::UserAgent;
|
||||
use threads::shared qw(shared_clone);
|
||||
use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
|
||||
:panel :sizer :toolbar :window wxTheApp :notebook :combobox);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
|
||||
:panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
|
||||
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
|
||||
EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED);
|
||||
use base 'Wx::Panel';
|
||||
|
@ -30,6 +31,7 @@ use constant TB_SCALE => &Wx::NewId;
|
|||
use constant TB_SPLIT => &Wx::NewId;
|
||||
use constant TB_CUT => &Wx::NewId;
|
||||
use constant TB_SETTINGS => &Wx::NewId;
|
||||
use constant TB_LAYER_EDITING => &Wx::NewId;
|
||||
|
||||
# package variables to avoid passing lexicals to threads
|
||||
our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType;
|
||||
|
@ -94,7 +96,7 @@ sub new {
|
|||
|
||||
# Initialize 3D plater
|
||||
if ($Slic3r::GUI::have_OpenGL) {
|
||||
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config});
|
||||
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config});
|
||||
$self->{preview_notebook}->AddPage($self->{canvas3D}, '3D');
|
||||
$self->{canvas3D}->set_on_select_object($on_select_object);
|
||||
$self->{canvas3D}->set_on_double_click($on_double_click);
|
||||
|
@ -154,6 +156,10 @@ sub new {
|
|||
$self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), '');
|
||||
$self->{htoolbar}->AddSeparator;
|
||||
$self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), '');
|
||||
|
||||
# FIXME add a button for layer editing
|
||||
$self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, undef, 'Layer Editing')
|
||||
if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1);
|
||||
} else {
|
||||
my %tbar_buttons = (
|
||||
add => "Add…",
|
||||
|
@ -168,12 +174,17 @@ sub new {
|
|||
split => "Split",
|
||||
cut => "Cut…",
|
||||
settings => "Settings…",
|
||||
layer_editing => "Layer editing",
|
||||
);
|
||||
$self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) {
|
||||
$self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
|
||||
$self->{btoolbar}->Add($self->{"btn_$_"});
|
||||
}
|
||||
if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1) {
|
||||
$self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
|
||||
$self->{btoolbar}->Add($self->{"btn_layer_editing"});
|
||||
}
|
||||
}
|
||||
|
||||
$self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize,
|
||||
|
@ -256,6 +267,11 @@ sub new {
|
|||
EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; });
|
||||
EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog });
|
||||
EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog });
|
||||
EVT_TOOL($self, TB_LAYER_EDITING, sub {
|
||||
my $state = $self->{canvas3D}->layer_editing_enabled;
|
||||
$self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state);
|
||||
$self->on_layer_editing_toggled(! $state);
|
||||
});
|
||||
} else {
|
||||
EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; });
|
||||
EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove
|
||||
|
@ -269,6 +285,7 @@ sub new {
|
|||
EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; });
|
||||
EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog });
|
||||
EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog });
|
||||
EVT_TOGGLEBUTTON($self, $self->{btn_layer_editing}, sub { $self->on_layer_editing_toggled($self->{btn_layer_editing}->GetValue); });
|
||||
}
|
||||
|
||||
$_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
|
||||
|
@ -464,6 +481,12 @@ sub _on_select_preset {
|
|||
$self->on_config_change($self->GetFrame->config);
|
||||
}
|
||||
|
||||
sub on_layer_editing_toggled {
|
||||
my ($self, $new_state) = @_;
|
||||
$self->{canvas3D}->layer_editing_enabled($new_state);
|
||||
$self->{canvas3D}->update;
|
||||
}
|
||||
|
||||
sub GetFrame {
|
||||
my ($self) = @_;
|
||||
return &Wx::GetTopLevelParent($self);
|
||||
|
@ -1481,6 +1504,8 @@ sub on_thumbnail_made {
|
|||
# (i.e. when an object is added/removed/moved/rotated/scaled)
|
||||
sub update {
|
||||
my ($self, $force_autocenter) = @_;
|
||||
|
||||
print "Platter - update\n";
|
||||
|
||||
if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) {
|
||||
$self->{model}->center_instances_around_point($self->bed_centerf);
|
||||
|
@ -1679,7 +1704,7 @@ sub object_list_changed {
|
|||
my $have_objects = @{$self->{objects}} ? 1 : 0;
|
||||
my $method = $have_objects ? 'Enable' : 'Disable';
|
||||
$self->{"btn_$_"}->$method
|
||||
for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode);
|
||||
for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode layer_editing);
|
||||
|
||||
if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) {
|
||||
$self->{btn_reslice}->Disable;
|
||||
|
@ -1712,6 +1737,7 @@ sub selection_changed {
|
|||
if ($self->{object_info_size}) { # have we already loaded the info pane?
|
||||
if ($have_sel) {
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
$model_object->print_info;
|
||||
my $model_instance = $model_object->instances->[0];
|
||||
$self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size}));
|
||||
$self->{object_info_materials}->SetLabel($model_object->materials_count);
|
||||
|
|
|
@ -173,7 +173,7 @@ sub repaint {
|
|||
|
||||
# if sequential printing is enabled and we have more than one object, draw clearance area
|
||||
if ($self->{config}->complete_objects && (map @{$_->instances}, @{$self->{model}->objects}) > 1) {
|
||||
my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), 1, JT_ROUND, scale(0.1))};
|
||||
my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), JT_ROUND, scale(0.1))};
|
||||
$dc->SetPen($self->{clearance_pen});
|
||||
$dc->SetBrush($self->{transparent_brush});
|
||||
$dc->DrawPolygon($self->scaled_points_to_pixel($clearance, 1), 0, 0);
|
||||
|
@ -185,7 +185,7 @@ sub repaint {
|
|||
if (@{$self->{objects}} && $self->{config}->skirts) {
|
||||
my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$self->{objects}};
|
||||
if (@points >= 3) {
|
||||
my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), 1, JT_ROUND, scale(0.1))};
|
||||
my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), JT_ROUND, scale(0.1))};
|
||||
$dc->SetPen($self->{skirt_pen});
|
||||
$dc->SetBrush($self->{transparent_brush});
|
||||
$dc->DrawPolygon($self->scaled_points_to_pixel($convex_hull, 1), 0, 0);
|
||||
|
|
|
@ -12,7 +12,7 @@ use base qw(Slic3r::GUI::3DScene Class::Accessor);
|
|||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $objects, $model, $config) = @_;
|
||||
my ($parent, $objects, $model, $print, $config) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent);
|
||||
$self->enable_picking(1);
|
||||
|
@ -22,6 +22,7 @@ sub new {
|
|||
|
||||
$self->{objects} = $objects;
|
||||
$self->{model} = $model;
|
||||
$self->{print} = $print;
|
||||
$self->{config} = $config;
|
||||
$self->{on_select_object} = sub {};
|
||||
$self->{on_instances_moved} = sub {};
|
||||
|
@ -31,7 +32,7 @@ sub new {
|
|||
|
||||
my $obj_idx = undef;
|
||||
if ($volume_idx != -1) {
|
||||
$obj_idx = $self->object_idx($volume_idx);
|
||||
$obj_idx = $self->volumes->[$volume_idx]->object_idx;
|
||||
}
|
||||
$self->{on_select_object}->($obj_idx)
|
||||
if $self->{on_select_object};
|
||||
|
@ -42,8 +43,8 @@ sub new {
|
|||
my %done = (); # prevent moving instances twice
|
||||
foreach my $volume_idx (@volume_idxs) {
|
||||
my $volume = $self->volumes->[$volume_idx];
|
||||
my $obj_idx = $self->object_idx($volume_idx);
|
||||
my $instance_idx = $self->instance_idx($volume_idx);
|
||||
my $obj_idx = $volume->object_idx;
|
||||
my $instance_idx = $volume->instance_idx;
|
||||
next if $done{"${obj_idx}_${instance_idx}"};
|
||||
$done{"${obj_idx}_${instance_idx}"} = 1;
|
||||
|
||||
|
@ -89,7 +90,7 @@ sub update {
|
|||
$self->update_bed_size;
|
||||
|
||||
foreach my $obj_idx (0..$#{$self->{model}->objects}) {
|
||||
my @volume_idxs = $self->load_object($self->{model}, $obj_idx);
|
||||
my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx);
|
||||
|
||||
if ($self->{objects}[$obj_idx]->selected) {
|
||||
$self->select_volume($_) for @volume_idxs;
|
||||
|
|
|
@ -114,7 +114,7 @@ sub new {
|
|||
my $canvas;
|
||||
if ($Slic3r::GUI::have_OpenGL) {
|
||||
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
|
||||
$canvas->load_object($self->{model_object}, undef, [0]);
|
||||
$canvas->load_object($self->{model_object}, undef, undef, [0]);
|
||||
$canvas->set_auto_bed_shape;
|
||||
$canvas->SetSize([500,500]);
|
||||
$canvas->SetMinSize($canvas->GetSize);
|
||||
|
@ -244,7 +244,7 @@ sub _update {
|
|||
}
|
||||
|
||||
$self->{canvas}->reset_objects;
|
||||
$self->{canvas}->load_object($_, undef, [0]) for @objects;
|
||||
$self->{canvas}->load_object($_, undef, undef, [0]) for @objects;
|
||||
$self->{canvas}->SetCuttingPlane(
|
||||
$self->{cut_options}{z},
|
||||
[@expolygons],
|
||||
|
|
|
@ -22,6 +22,7 @@ sub new {
|
|||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
|
||||
my $object = $self->{model_object} = $params{model_object};
|
||||
my $print_object = $self->{print_object} = $params{print_object};
|
||||
|
||||
# create TreeCtrl
|
||||
my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100],
|
||||
|
@ -82,7 +83,7 @@ sub new {
|
|||
$self->reload_tree($canvas->volume_idx($volume_idx));
|
||||
});
|
||||
|
||||
$canvas->load_object($self->{model_object}, undef, [0]);
|
||||
$canvas->load_object($self->{model_object}, undef, undef, [0]);
|
||||
$canvas->set_auto_bed_shape;
|
||||
$canvas->SetSize([500,500]);
|
||||
$canvas->zoom_to_volumes;
|
||||
|
|
|
@ -5,9 +5,9 @@ use warnings;
|
|||
require Exporter;
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT_OK = qw(offset offset_ex
|
||||
diff_ex diff union_ex intersection_ex xor_ex JT_ROUND JT_MITER
|
||||
JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex
|
||||
intersection intersection_pl diff_pl union CLIPPER_OFFSET_SCALE
|
||||
union_pt_chained diff_ppl intersection_ppl);
|
||||
diff_ex diff union_ex intersection_ex JT_ROUND JT_MITER
|
||||
JT_SQUARE is_counter_clockwise offset2 offset2_ex
|
||||
intersection intersection_pl diff_pl union
|
||||
union_pt_chained);
|
||||
|
||||
1;
|
||||
|
|
|
@ -41,8 +41,12 @@ sub size {
|
|||
sub process {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->status_cb->(20, "Generating perimeters");
|
||||
$_->make_perimeters for @{$self->objects};
|
||||
|
||||
$self->status_cb->(70, "Infilling layers");
|
||||
$_->infill for @{$self->objects};
|
||||
|
||||
$_->generate_support_material for @{$self->objects};
|
||||
$self->make_skirt;
|
||||
$self->make_brim; # must come after make_skirt
|
||||
|
@ -276,7 +280,7 @@ sub make_skirt {
|
|||
my $distance = scale max($self->config->skirt_distance, $self->config->brim_width);
|
||||
for (my $i = $skirts; $i > 0; $i--) {
|
||||
$distance += scale $spacing;
|
||||
my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0];
|
||||
my $loop = offset([$convex_hull], $distance, JT_ROUND, scale(0.1))->[0];
|
||||
my $eloop = Slic3r::ExtrusionLoop->new_from_paths(
|
||||
Slic3r::ExtrusionPath->new(
|
||||
polyline => Slic3r::Polygon->new(@$loop)->split_at_first_point,
|
||||
|
@ -369,7 +373,7 @@ sub make_brim {
|
|||
# -0.5 because islands are not represented by their centerlines
|
||||
# (first offset more, then step back - reverse order than the one used for
|
||||
# perimeters because here we're offsetting outwards)
|
||||
push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, 100000, JT_SQUARE)};
|
||||
push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, JT_SQUARE)};
|
||||
}
|
||||
|
||||
$self->brim->append(map Slic3r::ExtrusionLoop->new_from_paths(
|
||||
|
|
|
@ -7,7 +7,7 @@ use List::Util qw(min max sum first);
|
|||
use Slic3r::Flow ':roles';
|
||||
use Slic3r::Geometry qw(X Y Z PI scale unscale chained_path epsilon);
|
||||
use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex
|
||||
offset offset_ex offset2 offset2_ex intersection_ppl CLIPPER_OFFSET_SCALE JT_MITER);
|
||||
offset offset_ex offset2 offset2_ex JT_MITER);
|
||||
use Slic3r::Print::State ':steps';
|
||||
use Slic3r::Surface ':types';
|
||||
|
||||
|
@ -45,223 +45,7 @@ sub slice {
|
|||
$self->set_step_started(STEP_SLICE);
|
||||
$self->print->status_cb->(10, "Processing triangulated mesh");
|
||||
|
||||
# init layers
|
||||
{
|
||||
$self->clear_layers;
|
||||
|
||||
# make layers taking custom heights into account
|
||||
my $id = 0;
|
||||
my $print_z = 0;
|
||||
my $first_object_layer_height = -1;
|
||||
my $first_object_layer_distance = -1;
|
||||
|
||||
# add raft layers
|
||||
if ($self->config->raft_layers > 0) {
|
||||
# Reserve object layers for the raft. Last layer of the raft is the contact layer.
|
||||
$id += $self->config->raft_layers;
|
||||
|
||||
# Raise first object layer Z by the thickness of the raft itself
|
||||
# plus the extra distance required by the support material logic.
|
||||
#FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case.
|
||||
my $first_layer_height = $self->config->get_value('first_layer_height');
|
||||
$print_z += $first_layer_height;
|
||||
|
||||
# Use as large as possible layer height for the intermediate raft layers.
|
||||
my $support_material_layer_height;
|
||||
{
|
||||
my @nozzle_diameters = (
|
||||
map $self->print->config->get_at('nozzle_diameter', $_),
|
||||
$self->config->support_material_extruder-1,
|
||||
$self->config->support_material_interface_extruder-1,
|
||||
);
|
||||
$support_material_layer_height = 0.75 * min(@nozzle_diameters);
|
||||
}
|
||||
$print_z += $support_material_layer_height * ($self->config->raft_layers - 1);
|
||||
|
||||
# compute the average of all nozzles used for printing the object
|
||||
#FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa?
|
||||
my $nozzle_diameter;
|
||||
{
|
||||
my @nozzle_diameters = (
|
||||
map $self->print->config->get_at('nozzle_diameter', $_), @{$self->print->object_extruders}
|
||||
);
|
||||
$nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;
|
||||
}
|
||||
$first_object_layer_distance = $self->_support_material->contact_distance($self->config->layer_height, $nozzle_diameter);
|
||||
|
||||
# force first layer print_z according to the contact distance
|
||||
# (the loop below will raise print_z by such height)
|
||||
$first_object_layer_height = $first_object_layer_distance - $self->config->support_material_contact_distance;
|
||||
}
|
||||
|
||||
# loop until we have at least one layer and the max slice_z reaches the object height
|
||||
my $slice_z = 0;
|
||||
my $height = 0;
|
||||
my $max_z = unscale($self->size->z);
|
||||
while (($slice_z - $height) <= $max_z) {
|
||||
# assign the default height to the layer according to the general settings
|
||||
$height = ($id == 0)
|
||||
? $self->config->get_value('first_layer_height')
|
||||
: $self->config->layer_height;
|
||||
|
||||
# look for an applicable custom range
|
||||
if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) {
|
||||
$height = $range->[2];
|
||||
|
||||
# if user set custom height to zero we should just skip the range and resume slicing over it
|
||||
if ($height == 0) {
|
||||
$slice_z += $range->[1] - $range->[0];
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
if ($first_object_layer_height != -1 && !@{$self->layers}) {
|
||||
$height = $first_object_layer_height;
|
||||
$print_z += ($first_object_layer_distance - $height);
|
||||
}
|
||||
|
||||
$print_z += $height;
|
||||
$slice_z += $height/2;
|
||||
|
||||
### Slic3r::debugf "Layer %d: height = %s; slice_z = %s; print_z = %s\n", $id, $height, $slice_z, $print_z;
|
||||
|
||||
$self->add_layer($id, $height, $print_z, $slice_z);
|
||||
if ($self->layer_count >= 2) {
|
||||
my $lc = $self->layer_count;
|
||||
$self->get_layer($lc - 2)->set_upper_layer($self->get_layer($lc - 1));
|
||||
$self->get_layer($lc - 1)->set_lower_layer($self->get_layer($lc - 2));
|
||||
}
|
||||
$id++;
|
||||
|
||||
$slice_z += $height/2; # add the other half layer
|
||||
}
|
||||
}
|
||||
|
||||
# make sure all layers contain layer region objects for all regions
|
||||
my $regions_count = $self->print->region_count;
|
||||
foreach my $layer (@{ $self->layers }) {
|
||||
$layer->region($_) for 0 .. ($regions_count-1);
|
||||
}
|
||||
|
||||
# get array of Z coordinates for slicing
|
||||
my @z = map $_->slice_z, @{$self->layers};
|
||||
|
||||
# slice all non-modifier volumes
|
||||
for my $region_id (0..($self->region_count - 1)) {
|
||||
my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 0);
|
||||
for my $layer_id (0..$#$expolygons_by_layer) {
|
||||
my $layerm = $self->get_layer($layer_id)->regions->[$region_id];
|
||||
$layerm->slices->clear;
|
||||
foreach my $expolygon (@{ $expolygons_by_layer->[$layer_id] }) {
|
||||
$layerm->slices->append(Slic3r::Surface->new(
|
||||
expolygon => $expolygon,
|
||||
surface_type => S_TYPE_INTERNAL,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# then slice all modifier volumes
|
||||
if ($self->region_count > 1) {
|
||||
for my $region_id (0..$self->region_count) {
|
||||
my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 1);
|
||||
|
||||
# loop through the other regions and 'steal' the slices belonging to this one
|
||||
for my $other_region_id (0..$self->region_count) {
|
||||
next if $other_region_id == $region_id;
|
||||
|
||||
for my $layer_id (0..$#$expolygons_by_layer) {
|
||||
my $layerm = $self->get_layer($layer_id)->regions->[$region_id];
|
||||
my $other_layerm = $self->get_layer($layer_id)->regions->[$other_region_id];
|
||||
next if !defined $other_layerm;
|
||||
|
||||
my $other_slices = [ map $_->p, @{$other_layerm->slices} ]; # Polygons
|
||||
my $my_parts = intersection_ex(
|
||||
$other_slices,
|
||||
[ map @$_, @{ $expolygons_by_layer->[$layer_id] } ],
|
||||
);
|
||||
next if !@$my_parts;
|
||||
|
||||
# append new parts to our region
|
||||
foreach my $expolygon (@$my_parts) {
|
||||
$layerm->slices->append(Slic3r::Surface->new(
|
||||
expolygon => $expolygon,
|
||||
surface_type => S_TYPE_INTERNAL,
|
||||
));
|
||||
}
|
||||
|
||||
# remove such parts from original region
|
||||
$other_layerm->slices->clear;
|
||||
$other_layerm->slices->append(Slic3r::Surface->new(
|
||||
expolygon => $_,
|
||||
surface_type => S_TYPE_INTERNAL,
|
||||
)) for @{ diff_ex($other_slices, [ map @$_, @$my_parts ]) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# remove last layer(s) if empty
|
||||
$self->delete_layer($self->layer_count - 1)
|
||||
while $self->layer_count && (!map @{$_->slices}, @{$self->get_layer($self->layer_count - 1)->regions});
|
||||
|
||||
foreach my $layer (@{ $self->layers }) {
|
||||
# apply size compensation
|
||||
if ($self->config->xy_size_compensation != 0) {
|
||||
my $delta = scale($self->config->xy_size_compensation);
|
||||
if (@{$layer->regions} == 1) {
|
||||
# single region
|
||||
my $layerm = $layer->regions->[0];
|
||||
my $slices = [ map $_->p, @{$layerm->slices} ];
|
||||
$layerm->slices->clear;
|
||||
$layerm->slices->append(Slic3r::Surface->new(
|
||||
expolygon => $_,
|
||||
surface_type => S_TYPE_INTERNAL,
|
||||
)) for @{offset_ex($slices, $delta)};
|
||||
} else {
|
||||
if ($delta < 0) {
|
||||
# multiple regions, shrinking
|
||||
# we apply the offset to the combined shape, then intersect it
|
||||
# with the original slices for each region
|
||||
my $slices = union([ map $_->p, map @{$_->slices}, @{$layer->regions} ]);
|
||||
$slices = offset($slices, $delta);
|
||||
foreach my $layerm (@{$layer->regions}) {
|
||||
my $this_slices = intersection_ex(
|
||||
$slices,
|
||||
[ map $_->p, @{$layerm->slices} ],
|
||||
);
|
||||
$layerm->slices->clear;
|
||||
$layerm->slices->append(Slic3r::Surface->new(
|
||||
expolygon => $_,
|
||||
surface_type => S_TYPE_INTERNAL,
|
||||
)) for @$this_slices;
|
||||
}
|
||||
} else {
|
||||
# multiple regions, growing
|
||||
# this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap
|
||||
# so we give priority to the first one and so on
|
||||
for my $i (0..$#{$layer->regions}) {
|
||||
my $layerm = $layer->regions->[$i];
|
||||
my $slices = offset_ex([ map $_->p, @{$layerm->slices} ], $delta);
|
||||
if ($i > 0) {
|
||||
$slices = diff_ex(
|
||||
[ map @$_, @$slices ],
|
||||
[ map $_->p, map @{$_->slices}, map $layer->regions->[$_], 0..($i-1) ], # slices of already processed regions
|
||||
);
|
||||
}
|
||||
$layerm->slices->clear;
|
||||
$layerm->slices->append(Slic3r::Surface->new(
|
||||
expolygon => $_,
|
||||
surface_type => S_TYPE_INTERNAL,
|
||||
)) for @$slices;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Merge all regions' slices to get islands, chain them by a shortest path.
|
||||
$layer->make_slices;
|
||||
}
|
||||
$self->_slice;
|
||||
|
||||
# detect slicing errors
|
||||
my $warning_thrown = 0;
|
||||
|
@ -334,151 +118,15 @@ sub slice {
|
|||
$self->set_step_done(STEP_SLICE);
|
||||
}
|
||||
|
||||
# called from slice()
|
||||
sub _slice_region {
|
||||
my ($self, $region_id, $z, $modifier) = @_;
|
||||
|
||||
return [] if !@{$self->get_region_volumes($region_id)};
|
||||
|
||||
# compose mesh
|
||||
my $mesh;
|
||||
foreach my $volume_id (@{ $self->get_region_volumes($region_id) }) {
|
||||
my $volume = $self->model_object->volumes->[$volume_id];
|
||||
next if $volume->modifier && !$modifier;
|
||||
next if !$volume->modifier && $modifier;
|
||||
|
||||
if (defined $mesh) {
|
||||
$mesh->merge($volume->mesh);
|
||||
} else {
|
||||
$mesh = $volume->mesh->clone;
|
||||
}
|
||||
}
|
||||
return if !defined $mesh;
|
||||
|
||||
# transform mesh
|
||||
# we ignore the per-instance transformations currently and only
|
||||
# consider the first one
|
||||
$self->model_object->instances->[0]->transform_mesh($mesh, 1);
|
||||
|
||||
# align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
|
||||
$mesh->translate((map unscale(-$_), @{$self->_copies_shift}), -$self->model_object->bounding_box->z_min);
|
||||
|
||||
# perform actual slicing
|
||||
return $mesh->slice($z);
|
||||
}
|
||||
|
||||
# 1) Merges typed region slices into stInternal type.
|
||||
# 2) Increases an "extra perimeters" counter at region slices where needed.
|
||||
# 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
|
||||
sub make_perimeters {
|
||||
my $self = shift;
|
||||
my ($self) = @_;
|
||||
|
||||
# prerequisites
|
||||
$self->slice;
|
||||
|
||||
return if $self->step_done(STEP_PERIMETERS);
|
||||
$self->set_step_started(STEP_PERIMETERS);
|
||||
$self->print->status_cb->(20, "Generating perimeters");
|
||||
|
||||
# Merge region slices if they were split into types.
|
||||
# FIXME this is using a safety offset, so the region slices will be slightly bigger with each iteration.
|
||||
if ($self->typed_slices) {
|
||||
$_->merge_slices for @{$self->layers};
|
||||
$self->set_typed_slices(0);
|
||||
$self->invalidate_step(STEP_PREPARE_INFILL);
|
||||
}
|
||||
|
||||
# compare each layer to the one below, and mark those slices needing
|
||||
# one additional inner perimeter, like the top of domed objects-
|
||||
|
||||
# this algorithm makes sure that at least one perimeter is overlapping
|
||||
# but we don't generate any extra perimeter if fill density is zero, as they would be floating
|
||||
# inside the object - infill_only_where_needed should be the method of choice for printing
|
||||
# hollow objects
|
||||
for my $region_id (0 .. ($self->print->region_count-1)) {
|
||||
my $region = $self->print->regions->[$region_id];
|
||||
my $region_perimeters = $region->config->perimeters;
|
||||
|
||||
next if !$region->config->extra_perimeters;
|
||||
next if $region_perimeters == 0;
|
||||
next if $region->config->fill_density == 0;
|
||||
|
||||
for my $i (0 .. ($self->layer_count - 2)) {
|
||||
my $layerm = $self->get_layer($i)->get_region($region_id);
|
||||
my $upper_layerm = $self->get_layer($i+1)->get_region($region_id);
|
||||
my $upper_layerm_polygons = [ map $_->p, @{$upper_layerm->slices} ];
|
||||
# Filter upper layer polygons in intersection_ppl by their bounding boxes?
|
||||
# my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
|
||||
my $total_loop_length = sum(map $_->length, @$upper_layerm_polygons) // 0;
|
||||
|
||||
my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
|
||||
my $ext_perimeter_flow = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER);
|
||||
my $ext_perimeter_width = $ext_perimeter_flow->scaled_width;
|
||||
my $ext_perimeter_spacing = $ext_perimeter_flow->scaled_spacing;
|
||||
|
||||
foreach my $slice (@{$layerm->slices}) {
|
||||
while (1) {
|
||||
# compute the total thickness of perimeters
|
||||
my $perimeters_thickness = $ext_perimeter_width/2 + $ext_perimeter_spacing/2
|
||||
+ ($region_perimeters-1 + $slice->extra_perimeters) * $perimeter_spacing;
|
||||
|
||||
# define a critical area where we don't want the upper slice to fall into
|
||||
# (it should either lay over our perimeters or outside this area)
|
||||
my $critical_area_depth = $perimeter_spacing*1.5;
|
||||
my $critical_area = diff(
|
||||
offset($slice->expolygon->arrayref, -$perimeters_thickness),
|
||||
offset($slice->expolygon->arrayref, -($perimeters_thickness + $critical_area_depth)),
|
||||
);
|
||||
|
||||
# check whether a portion of the upper slices falls inside the critical area
|
||||
my $intersection = intersection_ppl(
|
||||
$upper_layerm_polygons,
|
||||
$critical_area,
|
||||
);
|
||||
|
||||
# only add an additional loop if at least 30% of the slice loop would benefit from it
|
||||
my $total_intersection_length = sum(map $_->length, @$intersection) // 0;
|
||||
last unless $total_intersection_length > $total_loop_length*0.3;
|
||||
|
||||
if (0) {
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"extra.svg",
|
||||
no_arrows => 1,
|
||||
expolygons => union_ex($critical_area),
|
||||
polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
|
||||
);
|
||||
}
|
||||
|
||||
$slice->extra_perimeters($slice->extra_perimeters + 1);
|
||||
}
|
||||
Slic3r::debugf " adding %d more perimeter(s) at layer %d\n",
|
||||
$slice->extra_perimeters, $layerm->layer->id
|
||||
if $slice->extra_perimeters > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Slic3r::parallelize(
|
||||
threads => $self->print->config->threads,
|
||||
items => sub { 0 .. ($self->layer_count - 1) },
|
||||
thread_cb => sub {
|
||||
my $q = shift;
|
||||
while (defined (my $i = $q->dequeue)) {
|
||||
$self->get_layer($i)->make_perimeters;
|
||||
}
|
||||
},
|
||||
no_threads_cb => sub {
|
||||
$_->make_perimeters for @{$self->layers};
|
||||
},
|
||||
);
|
||||
|
||||
# simplify slices (both layer and region slices),
|
||||
# we only need the max resolution for perimeters
|
||||
### This makes this method not-idempotent, so we keep it disabled for now.
|
||||
###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
|
||||
|
||||
$self->set_step_done(STEP_PERIMETERS);
|
||||
$self->_make_perimeters;
|
||||
}
|
||||
|
||||
sub prepare_infill {
|
||||
|
@ -598,32 +246,7 @@ sub infill {
|
|||
|
||||
# prerequisites
|
||||
$self->prepare_infill;
|
||||
|
||||
return if $self->step_done(STEP_INFILL);
|
||||
$self->set_step_started(STEP_INFILL);
|
||||
$self->print->status_cb->(70, "Infilling layers");
|
||||
|
||||
Slic3r::parallelize(
|
||||
threads => $self->print->config->threads,
|
||||
items => sub { 0..$#{$self->layers} },
|
||||
thread_cb => sub {
|
||||
my $q = shift;
|
||||
while (defined (my $i = $q->dequeue)) {
|
||||
$self->get_layer($i)->make_fills;
|
||||
}
|
||||
},
|
||||
no_threads_cb => sub {
|
||||
foreach my $layer (@{$self->layers}) {
|
||||
$layer->make_fills;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
### we could free memory now, but this would make this step not idempotent
|
||||
### Vojtech: Cannot release the fill_surfaces, they are used by the support generator.
|
||||
### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
|
||||
|
||||
$self->set_step_done(STEP_INFILL);
|
||||
$self->_infill;
|
||||
}
|
||||
|
||||
sub generate_support_material {
|
||||
|
@ -863,7 +486,7 @@ sub discover_horizontal_shells {
|
|||
# and it's not wanted in a hollow print even if it would make sense when
|
||||
# obeying the solid shell count option strictly (DWIM!)
|
||||
my $margin = $neighbor_layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width;
|
||||
my $regularized = offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5);
|
||||
my $regularized = offset2($new_internal_solid, -$margin, +$margin, JT_MITER, 5);
|
||||
my $too_narrow = diff(
|
||||
$new_internal_solid,
|
||||
$regularized,
|
||||
|
@ -893,7 +516,7 @@ sub discover_horizontal_shells {
|
|||
# have the same angle, so the next shell would be grown even more and so on.
|
||||
my $too_narrow = diff(
|
||||
$new_internal_solid,
|
||||
offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5),
|
||||
offset2($new_internal_solid, -$margin, +$margin, JT_MITER, 5),
|
||||
1,
|
||||
);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use Slic3r::ExtrusionPath ':roles';
|
|||
use Slic3r::Flow ':roles';
|
||||
use Slic3r::Geometry qw(epsilon scale scaled_epsilon PI rad2deg deg2rad convex_hull);
|
||||
use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2
|
||||
intersection_pl offset2_ex diff_pl CLIPPER_OFFSET_SCALE JT_MITER JT_ROUND);
|
||||
intersection_pl offset2_ex diff_pl JT_MITER JT_ROUND);
|
||||
use Slic3r::Surface ':types';
|
||||
|
||||
has 'print_config' => (is => 'rw', required => 1);
|
||||
|
@ -299,9 +299,8 @@ sub contact_area {
|
|||
offset(
|
||||
$diff,
|
||||
$_,
|
||||
CLIPPER_OFFSET_SCALE,
|
||||
JT_ROUND,
|
||||
scale(0.05)*CLIPPER_OFFSET_SCALE),
|
||||
scale(0.05)),
|
||||
$slices_margin
|
||||
);
|
||||
}
|
||||
|
@ -584,9 +583,8 @@ sub generate_base_layers {
|
|||
$fillet_radius_scaled,
|
||||
-$fillet_radius_scaled,
|
||||
# Use a geometric offsetting for filleting.
|
||||
CLIPPER_OFFSET_SCALE,
|
||||
JT_ROUND,
|
||||
0.2*$fillet_radius_scaled*CLIPPER_OFFSET_SCALE),
|
||||
0.2*$fillet_radius_scaled),
|
||||
$trim_polygons,
|
||||
0); # don't apply the safety offset.
|
||||
}
|
||||
|
|
23
xs/Build.PL
23
xs/Build.PL
|
@ -119,15 +119,19 @@ if (defined $ENV{BOOST_LIBRARYDIR}) {
|
|||
my $have_boost = 0;
|
||||
my @boost_libraries = qw(system thread log); # we need these
|
||||
|
||||
# check without explicit lib path (works on Linux)
|
||||
if (! $mswin) {
|
||||
$have_boost = 1
|
||||
if check_lib(
|
||||
lib => [ map "boost_${_}", @boost_libraries ],
|
||||
);
|
||||
if (!$ENV{SLIC3R_STATIC}) {
|
||||
# Dynamic linking of boost libraries.
|
||||
push @cflags, qw(-DBOOST_LOG_DYN_LINK);
|
||||
if (! $mswin) {
|
||||
# Check without explicit lib path (works on Linux and OSX).
|
||||
$have_boost = 1
|
||||
if check_lib(
|
||||
lib => [ map "boost_${_}", @boost_libraries ],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$ENV{SLIC3R_STATIC} && $have_boost) {
|
||||
if ($have_boost) {
|
||||
# The boost library was detected by check_lib on Linux.
|
||||
push @LIBS, map "-lboost_${_}", @boost_libraries;
|
||||
} else {
|
||||
|
@ -138,7 +142,6 @@ if (!$ENV{SLIC3R_STATIC} && $have_boost) {
|
|||
# Try to find the boost system library.
|
||||
my @files = glob "$path/${lib_prefix}system*$lib_ext";
|
||||
next if !@files;
|
||||
|
||||
if ($files[0] =~ /\Q${lib_prefix}system\E([^.]*)\Q$lib_ext\E$/) {
|
||||
# Suffix contains the version number, the build type etc.
|
||||
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(
|
||||
module_name => 'Slic3r::XS',
|
||||
dist_abstract => 'XS code for Slic3r',
|
||||
|
|
|
@ -32,6 +32,8 @@ src/libslic3r/ExtrusionEntityCollection.cpp
|
|||
src/libslic3r/ExtrusionEntityCollection.hpp
|
||||
src/libslic3r/ExtrusionSimulator.cpp
|
||||
src/libslic3r/ExtrusionSimulator.hpp
|
||||
src/libslic3r/Fill/Fill.cpp
|
||||
src/libslic3r/Fill/Fill.hpp
|
||||
src/libslic3r/Fill/FillBase.cpp
|
||||
src/libslic3r/Fill/FillBase.hpp
|
||||
src/libslic3r/Fill/FillConcentric.cpp
|
||||
|
@ -54,6 +56,8 @@ src/libslic3r/GCodeSender.cpp
|
|||
src/libslic3r/GCodeSender.hpp
|
||||
src/libslic3r/GCodeWriter.cpp
|
||||
src/libslic3r/GCodeWriter.hpp
|
||||
src/libslic3r/GCode/Analyzer.cpp
|
||||
src/libslic3r/GCode/Analyzer.hpp
|
||||
src/libslic3r/GCode/PressureEqualizer.cpp
|
||||
src/libslic3r/GCode/PressureEqualizer.hpp
|
||||
src/libslic3r/Geometry.cpp
|
||||
|
@ -88,6 +92,10 @@ src/libslic3r/PrintConfig.cpp
|
|||
src/libslic3r/PrintConfig.hpp
|
||||
src/libslic3r/PrintObject.cpp
|
||||
src/libslic3r/PrintRegion.cpp
|
||||
src/libslic3r/Slicing.cpp
|
||||
src/libslic3r/Slicing.hpp
|
||||
src/libslic3r/SlicingAdaptive.cpp
|
||||
src/libslic3r/SlicingAdaptive.hpp
|
||||
src/libslic3r/SupportMaterial.cpp
|
||||
src/libslic3r/SupportMaterial.hpp
|
||||
src/libslic3r/Surface.cpp
|
||||
|
@ -99,6 +107,7 @@ src/libslic3r/SVG.hpp
|
|||
src/libslic3r/TriangleMesh.cpp
|
||||
src/libslic3r/TriangleMesh.hpp
|
||||
src/libslic3r/utils.cpp
|
||||
src/libslic3r/Utils.hpp
|
||||
src/perlglue.cpp
|
||||
src/poly2tri/common/shapes.cc
|
||||
src/poly2tri/common/shapes.h
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <boost/predef/detail/endian_compat.h>
|
||||
#include <boost/detail/endian.hpp>
|
||||
|
||||
#ifndef BOOST_LITTLE_ENDIAN
|
||||
#error "admesh works correctly on little endian machines only!"
|
||||
|
|
|
@ -43,23 +43,8 @@ stl_open(stl_file *stl, char *file) {
|
|||
|
||||
void
|
||||
stl_initialize(stl_file *stl) {
|
||||
stl->error = 0;
|
||||
stl->stats.degenerate_facets = 0;
|
||||
stl->stats.edges_fixed = 0;
|
||||
stl->stats.facets_added = 0;
|
||||
stl->stats.facets_removed = 0;
|
||||
stl->stats.facets_reversed = 0;
|
||||
stl->stats.normals_fixed = 0;
|
||||
stl->stats.number_of_parts = 0;
|
||||
stl->stats.original_num_facets = 0;
|
||||
stl->stats.number_of_facets = 0;
|
||||
stl->stats.facets_malloced = 0;
|
||||
memset(stl, 0, sizeof(stl_file));
|
||||
stl->stats.volume = -1.0;
|
||||
|
||||
stl->neighbors_start = NULL;
|
||||
stl->facet_start = NULL;
|
||||
stl->v_indices = NULL;
|
||||
stl->v_shared = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -270,6 +255,7 @@ stl_read(stl_file *stl, int first_facet, int first) {
|
|||
rewind(stl->fp);
|
||||
}
|
||||
|
||||
char normal_buf[3][32];
|
||||
for(i = first_facet; i < stl->stats.number_of_facets; i++) {
|
||||
if(stl->stats.type == binary)
|
||||
/* Read a single facet from a binary .STL file */
|
||||
|
@ -287,17 +273,25 @@ stl_read(stl_file *stl, int first_facet, int first) {
|
|||
fscanf(stl->fp, "endsolid\n");
|
||||
fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
|
||||
|
||||
if((fscanf(stl->fp, " facet normal %f %f %f\n", &facet.normal.x, &facet.normal.y, &facet.normal.z) + \
|
||||
fscanf(stl->fp, " outer loop\n") + \
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z) + \
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z) + \
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z) + \
|
||||
fscanf(stl->fp, " endloop\n") + \
|
||||
if((fscanf(stl->fp, " facet normal %31s %31s %31s\n", normal_buf[0], normal_buf[1], normal_buf[2]) +
|
||||
fscanf(stl->fp, " outer loop\n") +
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z) +
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z) +
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z) +
|
||||
fscanf(stl->fp, " endloop\n") +
|
||||
fscanf(stl->fp, " endfacet\n")) != 12) {
|
||||
perror("Something is syntactically very wrong with this ASCII STL!");
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
|
||||
if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 ||
|
||||
sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 ||
|
||||
sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) {
|
||||
// Normal was mangled. Maybe denormals or "not a number" were stored?
|
||||
// Just reset the normal and silently ignore it.
|
||||
memset(&facet.normal, 0, sizeof(facet.normal));
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
|
|
@ -153,17 +153,35 @@ private:
|
|||
PolyNode* GetNextSiblingUp() const;
|
||||
void AddChild(PolyNode& child);
|
||||
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
|
||||
{
|
||||
public:
|
||||
~PolyTree(){Clear();};
|
||||
PolyTree() {}
|
||||
PolyTree(PolyTree &&src) { *this = std::move(src); }
|
||||
virtual ~PolyTree(){Clear();};
|
||||
PolyTree& operator=(PolyTree &&src) {
|
||||
AllNodes = std::move(src.AllNodes);
|
||||
Contour = std::move(src.Contour);
|
||||
Childs = std::move(src.Childs);
|
||||
Parent = nullptr;
|
||||
Index = src.Index;
|
||||
m_IsOpen = src.m_IsOpen;
|
||||
m_jointype = src.m_jointype;
|
||||
m_endtype = src.m_endtype;
|
||||
for (size_t i = 0; i < Childs.size(); ++ i)
|
||||
Childs[i]->Parent = this;
|
||||
return *this;
|
||||
}
|
||||
PolyNode* GetFirst() const;
|
||||
void Clear();
|
||||
int Total() const;
|
||||
private:
|
||||
PolyTree(const PolyTree &src) = delete;
|
||||
PolyTree& operator=(const PolyTree &src) = delete;
|
||||
PolyNodes AllNodes;
|
||||
friend class Clipper; //to access AllNodes
|
||||
};
|
||||
|
|
|
@ -277,4 +277,26 @@ BoundingBoxBase<PointClass>::overlap(const BoundingBoxBase<PointClass> &other) c
|
|||
template bool BoundingBoxBase<Point>::overlap(const BoundingBoxBase<Point> &point) const;
|
||||
template bool BoundingBoxBase<Pointf>::overlap(const BoundingBoxBase<Pointf> &point) const;
|
||||
|
||||
|
||||
// Align a coordinate to a grid. The coordinate may be negative,
|
||||
// the aligned value will never be bigger than the original one.
|
||||
static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
|
||||
// Current C++ standard defines the result of integer division to be rounded to zero,
|
||||
// for both positive and negative numbers. Here we want to round down for negative
|
||||
// numbers as well.
|
||||
coord_t aligned = (coord < 0) ?
|
||||
((coord - spacing + 1) / spacing) * spacing :
|
||||
(coord / spacing) * spacing;
|
||||
assert(aligned <= coord);
|
||||
return aligned;
|
||||
}
|
||||
|
||||
void BoundingBox::align_to_grid(const coord_t cell_size)
|
||||
{
|
||||
if (this->defined) {
|
||||
min.x = _align_to_grid(min.x, cell_size);
|
||||
min.y = _align_to_grid(min.y, cell_size);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -65,6 +65,9 @@ class BoundingBox : public BoundingBoxBase<Point>
|
|||
BoundingBox rotated(double angle, const Point ¢er) const;
|
||||
void rotate(double angle) { (*this) = this->rotated(angle); }
|
||||
void rotate(double angle, const Point ¢er) { (*this) = this->rotated(angle, center); }
|
||||
// Align the min corner to a grid of cell_size x cell_size cells,
|
||||
// to encompass the original bounding box.
|
||||
void align_to_grid(const coord_t cell_size);
|
||||
|
||||
BoundingBox() : BoundingBoxBase<Point>() {};
|
||||
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {};
|
||||
|
|
|
@ -40,13 +40,13 @@ void BridgeDetector::initialize()
|
|||
this->angle = -1.;
|
||||
|
||||
// Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
|
||||
Polygons grown = offset(this->expolygons, float(this->spacing));
|
||||
Polygons grown = offset(to_polygons(this->expolygons), float(this->spacing));
|
||||
|
||||
// Detect possible anchoring edges of this bridging region.
|
||||
// Detect what edges lie on lower slices by turning bridge contour and holes
|
||||
// into polylines and then clipping them with each lower slice's contour.
|
||||
// Currently _edges are only used to set a candidate direction of the bridge (see bridge_direction_candidates()).
|
||||
intersection(to_polylines(grown), this->lower_slices.contours(), &this->_edges);
|
||||
this->_edges = intersection_pl(to_polylines(grown), this->lower_slices.contours());
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf(" bridge has " PRINTF_ZU " support(s)\n", this->_edges.size());
|
||||
|
@ -117,7 +117,7 @@ BridgeDetector::detect_angle()
|
|||
double total_length = 0;
|
||||
double max_length = 0;
|
||||
{
|
||||
Lines clipped_lines = intersection(lines, clip_area);
|
||||
Lines clipped_lines = intersection_ln(lines, clip_area);
|
||||
for (size_t i = 0; i < clipped_lines.size(); ++i) {
|
||||
const Line &line = clipped_lines[i];
|
||||
if (expolygons_contain(this->_anchor_regions, line.a) && expolygons_contain(this->_anchor_regions, line.b)) {
|
||||
|
@ -203,76 +203,72 @@ std::vector<double> BridgeDetector::bridge_direction_candidates() const
|
|||
return angles;
|
||||
}
|
||||
|
||||
void
|
||||
BridgeDetector::coverage(double angle, Polygons* coverage) const
|
||||
Polygons BridgeDetector::coverage(double angle) const
|
||||
{
|
||||
if (angle == -1) angle = this->angle;
|
||||
if (angle == -1) return;
|
||||
if (angle == -1)
|
||||
angle = this->angle;
|
||||
|
||||
// Get anchors, convert them to Polygons and rotate them.
|
||||
Polygons anchors = to_polygons(this->_anchor_regions);
|
||||
polygons_rotate(anchors, PI/2.0 - angle);
|
||||
|
||||
Polygons covered;
|
||||
for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly)
|
||||
{
|
||||
// Clone our expolygon and rotate it so that we work with vertical lines.
|
||||
ExPolygon expolygon = *it_expoly;
|
||||
expolygon.rotate(PI/2.0 - angle);
|
||||
|
||||
if (angle != -1) {
|
||||
|
||||
// Get anchors, convert them to Polygons and rotate them.
|
||||
Polygons anchors = to_polygons(this->_anchor_regions);
|
||||
polygons_rotate(anchors, PI/2.0 - angle);
|
||||
|
||||
/* Outset the bridge expolygon by half the amount we used for detecting anchors;
|
||||
we'll use this one to generate our trapezoids and be sure that their vertices
|
||||
are inside the anchors and not on their contours leading to false negatives. */
|
||||
ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing));
|
||||
|
||||
// Compute trapezoids according to a vertical orientation
|
||||
Polygons trapezoids;
|
||||
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
|
||||
it->get_trapezoids2(&trapezoids, PI/2.0);
|
||||
|
||||
for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
|
||||
Lines supported = intersection(trapezoid->lines(), anchors);
|
||||
size_t n_supported = 0;
|
||||
// not nice, we need a more robust non-numeric check
|
||||
for (size_t i = 0; i < supported.size(); ++i)
|
||||
if (supported[i].length() >= this->spacing)
|
||||
++ n_supported;
|
||||
if (n_supported >= 2)
|
||||
covered.push_back(STDMOVE(*trapezoid));
|
||||
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.
|
||||
ExPolygon expolygon = *it_expoly;
|
||||
expolygon.rotate(PI/2.0 - angle);
|
||||
|
||||
/* Outset the bridge expolygon by half the amount we used for detecting anchors;
|
||||
we'll use this one to generate our trapezoids and be sure that their vertices
|
||||
are inside the anchors and not on their contours leading to false negatives. */
|
||||
ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing));
|
||||
|
||||
// Compute trapezoids according to a vertical orientation
|
||||
Polygons trapezoids;
|
||||
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
|
||||
it->get_trapezoids2(&trapezoids, PI/2.0);
|
||||
|
||||
for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
|
||||
Lines supported = intersection_ln(trapezoid->lines(), anchors);
|
||||
size_t n_supported = 0;
|
||||
// not nice, we need a more robust non-numeric check
|
||||
for (size_t i = 0; i < supported.size(); ++i)
|
||||
if (supported[i].length() >= this->spacing)
|
||||
++ n_supported;
|
||||
if (n_supported >= 2)
|
||||
covered.push_back(STDMOVE(*trapezoid));
|
||||
}
|
||||
}
|
||||
|
||||
// Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
|
||||
// instead of exact overlaps.
|
||||
covered = union_(covered);
|
||||
|
||||
// Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
|
||||
polygons_rotate(covered, -(PI/2.0 - angle));
|
||||
covered = intersection(covered, to_polygons(this->expolygons));
|
||||
|
||||
/*
|
||||
if (0) {
|
||||
my @lines = map @{$_->lines}, @$trapezoids;
|
||||
$_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
|
||||
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"coverage_" . rad2deg($angle) . ".svg",
|
||||
expolygons => [$self->expolygon],
|
||||
green_expolygons => $self->_anchor_regions,
|
||||
red_expolygons => $coverage,
|
||||
lines => \@lines,
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
|
||||
// instead of exact overlaps.
|
||||
covered = union_(covered);
|
||||
|
||||
// Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
|
||||
polygons_rotate(covered, -(PI/2.0 - angle));
|
||||
intersection(covered, to_polygons(this->expolygons), coverage);
|
||||
|
||||
/*
|
||||
if (0) {
|
||||
my @lines = map @{$_->lines}, @$trapezoids;
|
||||
$_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
|
||||
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"coverage_" . rad2deg($angle) . ".svg",
|
||||
expolygons => [$self->expolygon],
|
||||
green_expolygons => $self->_anchor_regions,
|
||||
red_expolygons => $coverage,
|
||||
lines => \@lines,
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
Polygons
|
||||
BridgeDetector::coverage(double angle) const
|
||||
{
|
||||
Polygons pp;
|
||||
this->coverage(angle, &pp);
|
||||
return pp;
|
||||
return covered;
|
||||
}
|
||||
|
||||
/* This method returns the bridge edges (as polylines) that are not supported
|
||||
|
@ -288,9 +284,7 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
|
|||
|
||||
for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) {
|
||||
// get unsupported bridge edges (both contour and holes)
|
||||
Polylines unuspported_polylines;
|
||||
diff(to_polylines(*it_expoly), grown_lower, &unuspported_polylines);
|
||||
Lines unsupported_lines = to_lines(unuspported_polylines);
|
||||
Lines unsupported_lines = to_lines(diff_pl(to_polylines(*it_expoly), grown_lower));
|
||||
/* Split into individual segments and filter out edges parallel to the bridging angle
|
||||
TODO: angle tolerance should probably be based on segment length and flow width,
|
||||
so that we build supports whenever there's a chance that at least one or two bridge
|
||||
|
|
|
@ -32,7 +32,6 @@ public:
|
|||
BridgeDetector(ExPolygon _expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
|
||||
BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
|
||||
bool detect_angle();
|
||||
void coverage(double angle, Polygons* coverage) const;
|
||||
Polygons coverage(double angle = -1) const;
|
||||
void unsupported_edges(double angle, Polylines* unsupported) const;
|
||||
Polylines unsupported_edges(double angle = -1) const;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,151 +14,197 @@ using ClipperLib::jtSquare;
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library.
|
||||
//FIXME Vojtech: Better to use a power of 2 coefficient and to use bit shifts for scaling.
|
||||
// How about 2^17=131072?
|
||||
// By the way, is the scalling needed at all? Cura runs all the computation with a fixed point precision of 1um, while Slic3r scales to 1nm,
|
||||
// further scaling by 10e5 brings us to
|
||||
#define CLIPPER_OFFSET_SCALE 100000.0
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// legacy code from Clipper documentation
|
||||
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons);
|
||||
void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons);
|
||||
//-----------------------------------------------------------
|
||||
|
||||
void Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output);
|
||||
template <class T>
|
||||
void Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output);
|
||||
template <class T>
|
||||
void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output);
|
||||
template <class T>
|
||||
void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output);
|
||||
void ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output);
|
||||
|
||||
void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale);
|
||||
ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input);
|
||||
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input);
|
||||
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input);
|
||||
Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input);
|
||||
Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input);
|
||||
Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input);
|
||||
Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input);
|
||||
Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input);
|
||||
|
||||
// offset Polygons
|
||||
void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
|
||||
// This is a safe variant of the polygon offset, tailored for a single ExPolygon:
|
||||
// a single polygon with multiple non-overlapping holes.
|
||||
// Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours.
|
||||
void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit);
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta,
|
||||
double scale, ClipperLib::JoinType joinType, double miterLimit);
|
||||
ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
|
||||
ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
|
||||
inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
|
||||
|
||||
// offset Polylines
|
||||
void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
|
||||
double miterLimit = 3);
|
||||
void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
|
||||
double miterLimit = 3);
|
||||
void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
|
||||
double miterLimit = 3);
|
||||
inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); }
|
||||
|
||||
void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta,
|
||||
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
// offset expolygons and surfaces
|
||||
ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit);
|
||||
ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit);
|
||||
inline Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
|
||||
|
||||
void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
|
||||
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1,
|
||||
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1,
|
||||
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
|
||||
template <class T>
|
||||
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||
const Slic3r::Polygons &clip, T* retval, bool safety_offset_);
|
||||
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
||||
const Slic3r::Polygons &clip, ClipperLib::Paths* retval, bool safety_offset_);
|
||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||
const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_);
|
||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
|
||||
const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_);
|
||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
|
||||
const Slic3r::Polygons &clip, Slic3r::Polylines* retval);
|
||||
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject,
|
||||
const Slic3r::Polygons &clip, Slic3r::Lines* retval);
|
||||
Slic3r::Polygons _clipper(ClipperLib::ClipType clipType,
|
||||
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType,
|
||||
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
|
||||
const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
|
||||
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType,
|
||||
const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
|
||||
template <class SubjectType, class ResultType>
|
||||
void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false);
|
||||
// diff
|
||||
inline Slic3r::Polygons
|
||||
diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctDifference, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
template <class SubjectType, class ResultType>
|
||||
void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_ = false);
|
||||
inline Slic3r::ExPolygons
|
||||
diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctDifference, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
inline Slic3r::ExPolygons
|
||||
diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
|
||||
}
|
||||
|
||||
template <class SubjectType, class ClipType>
|
||||
Slic3r::ExPolygons diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_ = false);
|
||||
inline Slic3r::Polygons
|
||||
diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
|
||||
}
|
||||
|
||||
template <class SubjectType, class ResultType>
|
||||
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false);
|
||||
inline Slic3r::Polylines
|
||||
diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
template <class SubjectType>
|
||||
SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
inline Slic3r::Polylines
|
||||
diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
Slic3r::ExPolygons
|
||||
intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
inline Slic3r::Lines
|
||||
diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ln(ClipperLib::ctDifference, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
template <class SubjectType>
|
||||
bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
// intersection
|
||||
inline Slic3r::Polygons
|
||||
intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctIntersection, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval,
|
||||
bool safety_offset_ = false);
|
||||
inline Slic3r::ExPolygons
|
||||
intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctIntersection, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_ = false);
|
||||
inline Slic3r::ExPolygons
|
||||
intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
|
||||
}
|
||||
|
||||
Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset = false);
|
||||
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset = false);
|
||||
Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_offset = false);
|
||||
inline Slic3r::Polygons
|
||||
intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
|
||||
}
|
||||
|
||||
void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset = false);
|
||||
Slic3r::Polygons union_(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset = false);
|
||||
inline Slic3r::Polylines
|
||||
intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_ = false);
|
||||
void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_ = false);
|
||||
static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval);
|
||||
inline Slic3r::Polylines
|
||||
intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear = false);
|
||||
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear = false);
|
||||
inline Slic3r::Lines
|
||||
intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ln(ClipperLib::ctIntersection, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
// union
|
||||
inline Slic3r::Polygons
|
||||
union_(const Slic3r::Polygons &subject, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::Polygons
|
||||
union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctUnion, subject, subject2, safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::ExPolygons
|
||||
union_ex(const Slic3r::Polygons &subject, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::ExPolygons
|
||||
union_ex(const Slic3r::ExPolygons &subject, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::ExPolygons
|
||||
union_ex(const Slic3r::Surfaces &subject, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
|
||||
}
|
||||
|
||||
|
||||
ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false);
|
||||
Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false);
|
||||
void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval);
|
||||
|
||||
/* OTHER */
|
||||
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false);
|
||||
Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false);
|
||||
|
||||
void safety_offset(ClipperLib::Paths* paths);
|
||||
|
||||
|
@ -166,4 +212,4 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons);
|
|||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef slic3r_Config_hpp_
|
||||
#define slic3r_Config_hpp_
|
||||
|
||||
#include <assert.h>
|
||||
#include <map>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
|
@ -73,11 +74,8 @@ class ConfigOptionVector : public ConfigOptionVectorBase
|
|||
};
|
||||
|
||||
T get_at(size_t i) const {
|
||||
try {
|
||||
return this->values.at(i);
|
||||
} catch (const std::out_of_range& oor) {
|
||||
return this->values.front();
|
||||
}
|
||||
assert(! this->values.empty());
|
||||
return (i < this->values.size()) ? this->values[i] : this->values.front();
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <float.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
#include <wx/image.h>
|
||||
|
@ -109,7 +110,6 @@ void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resol
|
|||
void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
|
||||
{
|
||||
// 1) Measure the bounding box.
|
||||
m_bbox.defined = false;
|
||||
for (size_t i = 0; i < m_contours.size(); ++ i) {
|
||||
const Slic3r::Points &pts = *m_contours[i];
|
||||
for (size_t j = 0; j < pts.size(); ++ j)
|
||||
|
@ -1118,7 +1118,7 @@ float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const
|
|||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const {
|
||||
BoundingBox bbox;
|
||||
bbox.min = bbox.max = Point(pt.x - m_bbox.min.x, pt.y - m_bbox.min.y);
|
||||
|
@ -1222,6 +1222,114 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
|
|||
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
|
||||
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
|
||||
{
|
||||
|
|
|
@ -12,11 +12,14 @@
|
|||
namespace Slic3r {
|
||||
namespace EdgeGrid {
|
||||
|
||||
struct Grid
|
||||
class Grid
|
||||
{
|
||||
public:
|
||||
Grid();
|
||||
~Grid();
|
||||
|
||||
void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; }
|
||||
|
||||
void create(const Polygons &polygons, coord_t resolution);
|
||||
void create(const ExPolygon &expoly, coord_t resolution);
|
||||
void create(const ExPolygons &expolygons, coord_t resolution);
|
||||
|
@ -54,6 +57,9 @@ struct Grid
|
|||
const size_t rows() const { return m_rows; }
|
||||
const size_t cols() const { return m_cols; }
|
||||
|
||||
// For supports: Contours enclosing the rasterized edges.
|
||||
Polygons contours_simplified() const;
|
||||
|
||||
protected:
|
||||
struct Cell {
|
||||
Cell() : begin(0), end(0) {}
|
||||
|
@ -65,6 +71,18 @@ protected:
|
|||
#if 0
|
||||
bool line_cell_intersect(const Point &p1, const Point &p2, const Cell &cell);
|
||||
#endif
|
||||
bool cell_inside_or_crossing(int r, int c) const
|
||||
{
|
||||
if (r < 0 || r >= m_rows ||
|
||||
c < 0 || c >= m_cols)
|
||||
// The cell is outside the domain. Hoping that the contours were correctly oriented, so
|
||||
// there is a CCW outmost contour so the out of domain cells are outside.
|
||||
return false;
|
||||
const Cell &cell = m_cells[r * m_cols + c];
|
||||
return
|
||||
(cell.begin < cell.end) ||
|
||||
(! m_signed_distance_field.empty() && m_signed_distance_field[r * (m_cols + 1) + c] <= 0.f);
|
||||
}
|
||||
|
||||
// Bounding box around the contours.
|
||||
BoundingBox m_bbox;
|
||||
|
|
|
@ -99,9 +99,7 @@ ExPolygon::contains(const Line &line) const
|
|||
bool
|
||||
ExPolygon::contains(const Polyline &polyline) const
|
||||
{
|
||||
Polylines pl_out;
|
||||
diff((Polylines)polyline, *this, &pl_out);
|
||||
return pl_out.empty();
|
||||
return diff_pl((Polylines)polyline, *this).empty();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -115,8 +113,7 @@ ExPolygon::contains(const Polylines &polylines) const
|
|||
svg.draw_outline(*this);
|
||||
svg.draw(polylines, "blue");
|
||||
#endif
|
||||
Polylines pl_out;
|
||||
diff(polylines, *this, &pl_out);
|
||||
Polylines pl_out = diff_pl(polylines, *this);
|
||||
#if 0
|
||||
svg.draw(pl_out, "red");
|
||||
#endif
|
||||
|
@ -162,8 +159,7 @@ ExPolygon::overlaps(const ExPolygon &other) const
|
|||
svg.draw_outline(*this);
|
||||
svg.draw_outline(other, "blue");
|
||||
#endif
|
||||
Polylines pl_out;
|
||||
intersection((Polylines)other, *this, &pl_out);
|
||||
Polylines pl_out = intersection_pl((Polylines)other, *this);
|
||||
#if 0
|
||||
svg.draw(pl_out, "red");
|
||||
#endif
|
||||
|
@ -396,11 +392,8 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const
|
|||
poly[3].y = bb.max.y;
|
||||
|
||||
// intersect with this expolygon
|
||||
Polygons trapezoids;
|
||||
intersection<Polygons,Polygons>(poly, *this, &trapezoids);
|
||||
|
||||
// append results to return value
|
||||
polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end());
|
||||
polygons_append(*polygons, intersection(poly, to_polygons(*this)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,16 +427,13 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
|
|||
// convert polygons
|
||||
std::list<TPPLPoly> input;
|
||||
|
||||
Polygons pp = *this;
|
||||
simplify_polygons(pp, &pp, true);
|
||||
ExPolygons expp;
|
||||
union_(pp, &expp);
|
||||
ExPolygons expp = union_ex(simplify_polygons(to_polygons(*this), true));
|
||||
|
||||
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
|
||||
// contour
|
||||
{
|
||||
TPPLPoly p;
|
||||
p.Init(ex->contour.points.size());
|
||||
p.Init(int(ex->contour.points.size()));
|
||||
//printf(PRINTF_ZU "\n0\n", ex->contour.points.size());
|
||||
for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) {
|
||||
p[ point-ex->contour.points.begin() ].x = point->x;
|
||||
|
@ -480,8 +470,8 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
|
|||
Polygon p;
|
||||
p.points.resize(num_points);
|
||||
for (long i = 0; i < num_points; ++i) {
|
||||
p.points[i].x = (*poly)[i].x;
|
||||
p.points[i].y = (*poly)[i].y;
|
||||
p.points[i].x = coord_t((*poly)[i].x);
|
||||
p.points[i].y = coord_t((*poly)[i].y);
|
||||
}
|
||||
polygons->push_back(p);
|
||||
}
|
||||
|
@ -490,8 +480,7 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
|
|||
void
|
||||
ExPolygon::triangulate_p2t(Polygons* polygons) const
|
||||
{
|
||||
ExPolygons expp;
|
||||
simplify_polygons(*this, &expp, true);
|
||||
ExPolygons expp = simplify_polygons_ex(*this, true);
|
||||
|
||||
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
|
||||
// TODO: prevent duplicate points
|
||||
|
|
|
@ -293,37 +293,37 @@ extern bool remove_sticks(ExPolygon &poly);
|
|||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
template <>
|
||||
struct polygon_traits<ExPolygon> {
|
||||
struct polygon_traits<Slic3r::ExPolygon> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef Points::const_iterator iterator_type;
|
||||
typedef Point point_type;
|
||||
typedef Slic3r::Points::const_iterator iterator_type;
|
||||
typedef Slic3r::Point point_type;
|
||||
|
||||
// Get the begin iterator
|
||||
static inline iterator_type begin_points(const ExPolygon& t) {
|
||||
static inline iterator_type begin_points(const Slic3r::ExPolygon& t) {
|
||||
return t.contour.points.begin();
|
||||
}
|
||||
|
||||
// Get the end iterator
|
||||
static inline iterator_type end_points(const ExPolygon& t) {
|
||||
static inline iterator_type end_points(const Slic3r::ExPolygon& t) {
|
||||
return t.contour.points.end();
|
||||
}
|
||||
|
||||
// Get the number of sides of the polygon
|
||||
static inline std::size_t size(const ExPolygon& t) {
|
||||
static inline std::size_t size(const Slic3r::ExPolygon& t) {
|
||||
return t.contour.points.size();
|
||||
}
|
||||
|
||||
// Get the winding direction of the polygon
|
||||
static inline winding_direction winding(const ExPolygon& t) {
|
||||
static inline winding_direction winding(const Slic3r::ExPolygon& t) {
|
||||
return unknown_winding;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_mutable_traits<ExPolygon> {
|
||||
struct polygon_mutable_traits<Slic3r::ExPolygon> {
|
||||
//expects stl style iterators
|
||||
template <typename iT>
|
||||
static inline ExPolygon& set_points(ExPolygon& expolygon, iT input_begin, iT input_end) {
|
||||
static inline Slic3r::ExPolygon& set_points(Slic3r::ExPolygon& expolygon, iT input_begin, iT input_end) {
|
||||
expolygon.contour.points.assign(input_begin, input_end);
|
||||
// skip last point since Boost will set last point = first point
|
||||
expolygon.contour.points.pop_back();
|
||||
|
@ -333,27 +333,27 @@ namespace boost { namespace polygon {
|
|||
|
||||
|
||||
template <>
|
||||
struct geometry_concept<ExPolygon> { typedef polygon_with_holes_concept type; };
|
||||
struct geometry_concept<Slic3r::ExPolygon> { typedef polygon_with_holes_concept type; };
|
||||
|
||||
template <>
|
||||
struct polygon_with_holes_traits<ExPolygon> {
|
||||
typedef Polygons::const_iterator iterator_holes_type;
|
||||
typedef Polygon hole_type;
|
||||
static inline iterator_holes_type begin_holes(const ExPolygon& t) {
|
||||
struct polygon_with_holes_traits<Slic3r::ExPolygon> {
|
||||
typedef Slic3r::Polygons::const_iterator iterator_holes_type;
|
||||
typedef Slic3r::Polygon hole_type;
|
||||
static inline iterator_holes_type begin_holes(const Slic3r::ExPolygon& t) {
|
||||
return t.holes.begin();
|
||||
}
|
||||
static inline iterator_holes_type end_holes(const ExPolygon& t) {
|
||||
static inline iterator_holes_type end_holes(const Slic3r::ExPolygon& t) {
|
||||
return t.holes.end();
|
||||
}
|
||||
static inline unsigned int size_holes(const ExPolygon& t) {
|
||||
static inline unsigned int size_holes(const Slic3r::ExPolygon& t) {
|
||||
return (int)t.holes.size();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_with_holes_mutable_traits<ExPolygon> {
|
||||
struct polygon_with_holes_mutable_traits<Slic3r::ExPolygon> {
|
||||
template <typename iT>
|
||||
static inline ExPolygon& set_holes(ExPolygon& t, iT inputBegin, iT inputEnd) {
|
||||
static inline Slic3r::ExPolygon& set_holes(Slic3r::ExPolygon& t, iT inputBegin, iT inputEnd) {
|
||||
t.holes.assign(inputBegin, inputEnd);
|
||||
return t;
|
||||
}
|
||||
|
@ -361,32 +361,32 @@ namespace boost { namespace polygon {
|
|||
|
||||
//first we register CPolygonSet as a polygon set
|
||||
template <>
|
||||
struct geometry_concept<ExPolygons> { typedef polygon_set_concept type; };
|
||||
struct geometry_concept<Slic3r::ExPolygons> { typedef polygon_set_concept type; };
|
||||
|
||||
//next we map to the concept through traits
|
||||
template <>
|
||||
struct polygon_set_traits<ExPolygons> {
|
||||
struct polygon_set_traits<Slic3r::ExPolygons> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef ExPolygons::const_iterator iterator_type;
|
||||
typedef ExPolygons operator_arg_type;
|
||||
typedef Slic3r::ExPolygons::const_iterator iterator_type;
|
||||
typedef Slic3r::ExPolygons operator_arg_type;
|
||||
|
||||
static inline iterator_type begin(const ExPolygons& polygon_set) {
|
||||
static inline iterator_type begin(const Slic3r::ExPolygons& polygon_set) {
|
||||
return polygon_set.begin();
|
||||
}
|
||||
|
||||
static inline iterator_type end(const ExPolygons& polygon_set) {
|
||||
static inline iterator_type end(const Slic3r::ExPolygons& polygon_set) {
|
||||
return polygon_set.end();
|
||||
}
|
||||
|
||||
//don't worry about these, just return false from them
|
||||
static inline bool clean(const ExPolygons& polygon_set) { return false; }
|
||||
static inline bool sorted(const ExPolygons& polygon_set) { return false; }
|
||||
static inline bool clean(const Slic3r::ExPolygons& polygon_set) { return false; }
|
||||
static inline bool sorted(const Slic3r::ExPolygons& polygon_set) { return false; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_set_mutable_traits<ExPolygons> {
|
||||
struct polygon_set_mutable_traits<Slic3r::ExPolygons> {
|
||||
template <typename input_iterator_type>
|
||||
static inline void set(ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) {
|
||||
static inline void set(Slic3r::ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) {
|
||||
expolygons.assign(input_begin, input_end);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,19 +13,13 @@ namespace Slic3r {
|
|||
void
|
||||
ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
||||
{
|
||||
// perform clipping
|
||||
Polylines clipped;
|
||||
intersection<Polylines,Polylines>(this->polyline, collection, &clipped);
|
||||
return this->_inflate_collection(clipped, retval);
|
||||
this->_inflate_collection(intersection_pl(this->polyline, collection), retval);
|
||||
}
|
||||
|
||||
void
|
||||
ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
||||
{
|
||||
// perform clipping
|
||||
Polylines clipped;
|
||||
diff<Polylines,Polylines>(this->polyline, collection, &clipped);
|
||||
return this->_inflate_collection(clipped, retval);
|
||||
this->_inflate_collection(diff_pl(this->polyline, collection), retval);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -58,9 +52,7 @@ ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCo
|
|||
|
||||
void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
|
||||
{
|
||||
Polygons tmp;
|
||||
offset(this->polyline, &tmp, scale_(this->width/2) + scaled_epsilon);
|
||||
polygons_append(out, STDMOVE(tmp));
|
||||
polygons_append(out, offset(this->polyline, float(scale_(this->width/2)) + scaled_epsilon));
|
||||
}
|
||||
|
||||
void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const
|
||||
|
@ -68,9 +60,7 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale
|
|||
// Instantiating the Flow class to get the line spacing.
|
||||
// Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler.
|
||||
Flow flow(this->width, this->height, 0.f, this->is_bridge());
|
||||
Polygons tmp;
|
||||
offset(this->polyline, &tmp, 0.5f * flow.scaled_spacing() + scaled_epsilon);
|
||||
polygons_append(out, STDMOVE(tmp));
|
||||
polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon));
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -194,6 +194,26 @@ class ExtrusionLoop : public ExtrusionEntity
|
|||
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
|
||||
};
|
||||
|
||||
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
|
||||
{
|
||||
dst.reserve(dst.size() + polylines.size());
|
||||
for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
|
||||
dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height));
|
||||
dst.back().polyline = *it_polyline;
|
||||
}
|
||||
}
|
||||
|
||||
#if SLIC3R_CPPVER >= 11
|
||||
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
|
||||
{
|
||||
dst.reserve(dst.size() + polylines.size());
|
||||
for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
|
||||
dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height));
|
||||
dst.back().polyline = std::move(*it_polyline);
|
||||
}
|
||||
}
|
||||
#endif // SLIC3R_CPPVER >= 11
|
||||
|
||||
inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
|
||||
{
|
||||
dst.reserve(dst.size() + polylines.size());
|
||||
|
|
|
@ -803,7 +803,7 @@ void gcode_spread_points(
|
|||
const Cell &cell = cells[i];
|
||||
acc[cell.idx.y()][cell.idx.x()] = (1.f - cell.fraction_covered) * cell.volume + cell.fraction_covered * cell.area * height_avg;
|
||||
}
|
||||
} else if (simulationType == ExtrusionSimulationSpreadExcess) {
|
||||
} else if (simulationType == Slic3r::ExtrusionSimulationSpreadExcess) {
|
||||
// The volume under the circle does not fit.
|
||||
// 1) Fill the underfilled cells and remove them from the list.
|
||||
float volume_borrowed_total = 0.;
|
||||
|
|
|
@ -13,7 +13,7 @@ enum ExtrusionSimulationType
|
|||
ExtrusionSimulationDontSpread,
|
||||
ExtrisopmSimulationSpreadNotOverfilled,
|
||||
ExtrusionSimulationSpreadFull,
|
||||
ExtrusionSimulationSpreadExcess,
|
||||
ExtrusionSimulationSpreadExcess
|
||||
};
|
||||
|
||||
// An opaque class, to keep the boost stuff away from the header.
|
||||
|
|
|
@ -168,7 +168,7 @@ void Fill3DHoneycomb::_fill_surface_single(
|
|||
it->translate(bb.min.x, bb.min.y);
|
||||
|
||||
// clip pattern to boundaries
|
||||
intersection(polylines, (Polygons)expolygon, &polylines);
|
||||
polylines = intersection_pl(polylines, (Polygons)expolygon);
|
||||
|
||||
// connect lines
|
||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
|
|
|
@ -45,8 +45,7 @@ Fill* Fill::new_from_type(const std::string &type)
|
|||
Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
// Perform offset.
|
||||
Slic3r::ExPolygons expp;
|
||||
offset(surface->expolygon, &expp, -0.5*scale_(this->spacing));
|
||||
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(-0.5*scale_(this->spacing)));
|
||||
// Create the infills for each of the regions.
|
||||
Polylines polylines_out;
|
||||
for (size_t i = 0; i < expp.size(); ++ i)
|
||||
|
|
|
@ -33,7 +33,7 @@ void FillConcentric::_fill_surface_single(
|
|||
|
||||
// generate paths from the outermost to the innermost, to avoid
|
||||
// adhesion problems of the first central tiny loops
|
||||
union_pt_chained(loops, &loops, false);
|
||||
loops = union_pt_chained(loops, false);
|
||||
|
||||
// split paths using a nearest neighbor search
|
||||
size_t iPathFirst = polylines_out.size();
|
||||
|
|
|
@ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single(
|
|||
Polylines p;
|
||||
for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
|
||||
p.push_back((Polyline)(*it));
|
||||
intersection(p, (Polygons)expolygon, &paths);
|
||||
paths = intersection_pl(p, to_polygons(expolygon));
|
||||
}
|
||||
|
||||
// connect paths
|
||||
|
@ -122,7 +122,7 @@ void FillHoneycomb::_fill_surface_single(
|
|||
}
|
||||
|
||||
// clip paths again to prevent connection segments from crossing the expolygon boundaries
|
||||
intersection(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)), &paths);
|
||||
paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)));
|
||||
// Move the polylines to the output, avoid a deep copy.
|
||||
size_t j = polylines_out.size();
|
||||
polylines_out.resize(j + paths.size(), Polyline());
|
||||
|
|
|
@ -44,7 +44,7 @@ void FillPlanePath::_fill_surface_single(
|
|||
coord_t(floor(it->x * distance_between_lines + 0.5)),
|
||||
coord_t(floor(it->y * distance_between_lines + 0.5))));
|
||||
// intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
|
||||
intersection(polylines, (Polygons)expolygon, &polylines);
|
||||
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
||||
|
||||
/*
|
||||
if (1) {
|
||||
|
|
|
@ -63,7 +63,7 @@ void FillRectilinear::_fill_surface_single(
|
|||
pts.push_back(it->a);
|
||||
pts.push_back(it->b);
|
||||
}
|
||||
Polylines polylines = intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), false);
|
||||
Polylines polylines = intersection_pl(polylines_src, offset(to_polygons(expolygon), scale_(0.02)), false);
|
||||
|
||||
// FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
|
||||
const float INFILL_OVERLAP_OVER_SPACING = 0.3f;
|
||||
|
|
|
@ -372,11 +372,9 @@ public:
|
|||
bool sticks_removed = remove_sticks(polygons_src);
|
||||
// if (sticks_removed) printf("Sticks removed!\n");
|
||||
polygons_outer = offset(polygons_src, aoffset1,
|
||||
CLIPPER_OFFSET_SCALE,
|
||||
ClipperLib::jtMiter,
|
||||
mitterLimit);
|
||||
polygons_inner = offset(polygons_outer, aoffset2 - aoffset1,
|
||||
CLIPPER_OFFSET_SCALE,
|
||||
ClipperLib::jtMiter,
|
||||
mitterLimit);
|
||||
// Filter out contours with zero area or small area, contours with 2 points only.
|
||||
|
|
|
@ -315,8 +315,7 @@ GCode::change_layer(const Layer &layer)
|
|||
|
||||
// avoid computing islands and overhangs if they're not needed
|
||||
if (this->config.avoid_crossing_perimeters) {
|
||||
ExPolygons islands;
|
||||
union_(layer.slices, &islands, true);
|
||||
ExPolygons islands = union_ex(layer.slices, true);
|
||||
this->avoid_crossing_perimeters.init_layer_mp(islands);
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ Layer::make_slices()
|
|||
FOREACH_LAYERREGION(this, layerm) {
|
||||
polygons_append(slices_p, to_polygons((*layerm)->slices));
|
||||
}
|
||||
union_(slices_p, &slices);
|
||||
slices = union_ex(slices_p);
|
||||
}
|
||||
|
||||
this->slices.expolygons.clear();
|
||||
|
@ -132,15 +132,11 @@ Layer::merge_slices()
|
|||
if (this->regions.size() == 1) {
|
||||
// Optimization, also more robust. Don't merge classified pieces of layerm->slices,
|
||||
// but use the non-split islands of a layer. For a single region print, these shall be equal.
|
||||
this->regions.front()->slices.surfaces.clear();
|
||||
surfaces_append(this->regions.front()->slices.surfaces, this->slices.expolygons, stInternal);
|
||||
this->regions.front()->slices.set(this->slices.expolygons, stInternal);
|
||||
} else {
|
||||
FOREACH_LAYERREGION(this, layerm) {
|
||||
ExPolygons expp;
|
||||
// without safety offset, artifacts are generated (GH #2494)
|
||||
union_(to_polygons(STDMOVE((*layerm)->slices.surfaces)), &expp, true);
|
||||
(*layerm)->slices.surfaces.clear();
|
||||
surfaces_append((*layerm)->slices.surfaces, expp, stInternal);
|
||||
(*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +219,7 @@ Layer::make_perimeters()
|
|||
}
|
||||
// merge the surfaces assigned to each group
|
||||
for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it)
|
||||
surfaces_append(new_slices.surfaces, union_ex(it->second, true), it->second.front());
|
||||
new_slices.append(union_ex(it->second, true), it->second.front());
|
||||
}
|
||||
|
||||
// make perimeters
|
||||
|
@ -236,8 +232,7 @@ Layer::make_perimeters()
|
|||
// Separate the fill surfaces.
|
||||
ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices);
|
||||
(*l)->fill_expolygons = expp;
|
||||
(*l)->fill_surfaces.surfaces.clear();
|
||||
surfaces_append((*l)->fill_surfaces.surfaces, STDMOVE(expp), fill_surfaces.surfaces.front());
|
||||
(*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,6 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
|
||||
typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
|
||||
|
||||
class Layer;
|
||||
class PrintRegion;
|
||||
class PrintObject;
|
||||
|
|
|
@ -52,8 +52,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
|
|||
Polygons fill_boundaries = to_polygons(this->fill_expolygons);
|
||||
this->fill_surfaces.surfaces.clear();
|
||||
for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++ surface)
|
||||
surfaces_append(
|
||||
this->fill_surfaces.surfaces,
|
||||
this->fill_surfaces.append(
|
||||
intersection_ex(to_polygons(surface->expolygon), fill_boundaries),
|
||||
surface->surface_type);
|
||||
}
|
||||
|
@ -91,9 +90,9 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection*
|
|||
g.process();
|
||||
}
|
||||
|
||||
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 3.
|
||||
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 1.5
|
||||
#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare, 0.
|
||||
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
|
||||
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
|
||||
#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
|
||||
|
||||
void
|
||||
LayerRegion::process_external_surfaces(const Layer* lower_layer)
|
||||
|
@ -194,7 +193,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
|
|||
break;
|
||||
}
|
||||
// Grown by 3mm.
|
||||
Polygons polys = offset(bridges[i].expolygon, float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS);
|
||||
Polygons polys = offset(to_polygons(bridges[i].expolygon), float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS);
|
||||
if (idx_island == -1) {
|
||||
printf("Bridge did not fall into the source region!\r\n");
|
||||
} else {
|
||||
|
@ -262,9 +261,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
|
|||
BridgeDetector bd(
|
||||
initial,
|
||||
lower_layer->slices,
|
||||
//FIXME parameters are not correct!
|
||||
// flow(FlowRole role, bool bridge = false, double width = -1) const;
|
||||
this->flow(frInfill, true, this->layer()->height).scaled_width()
|
||||
this->flow(frInfill, true).scaled_width()
|
||||
);
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id());
|
||||
|
|
|
@ -76,20 +76,20 @@ class Linef3
|
|||
void scale(double factor);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Slic3r
|
||||
|
||||
// start Boost
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
template <>
|
||||
struct geometry_concept<Line> { typedef segment_concept type; };
|
||||
struct geometry_concept<Slic3r::Line> { typedef segment_concept type; };
|
||||
|
||||
template <>
|
||||
struct segment_traits<Line> {
|
||||
struct segment_traits<Slic3r::Line> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef Point point_type;
|
||||
typedef Slic3r::Point point_type;
|
||||
|
||||
static inline point_type get(const Line& line, direction_1d dir) {
|
||||
static inline point_type get(const Slic3r::Line& line, direction_1d dir) {
|
||||
return dir.to_int() ? line.b : line.a;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "Layer.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "Slicing.hpp"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
|
|
@ -142,7 +142,7 @@ MotionPlanner::shortest_path(const Point &from, const Point &to)
|
|||
{
|
||||
// grow our environment slightly in order for simplify_by_visibility()
|
||||
// to work best by considering moves on boundaries valid as well
|
||||
ExPolygonCollection grown_env(offset_ex(env.env, +SCALED_EPSILON));
|
||||
ExPolygonCollection grown_env(offset_ex(env.env.expolygons, +SCALED_EPSILON));
|
||||
|
||||
if (island_idx == -1) {
|
||||
/* If 'from' or 'to' are not inside our env, they were connected using the
|
||||
|
@ -155,12 +155,12 @@ MotionPlanner::shortest_path(const Point &from, const Point &to)
|
|||
if (!grown_env.contains(from)) {
|
||||
// delete second point while the line connecting first to third crosses the
|
||||
// boundaries as many times as the current first to second
|
||||
while (polyline.points.size() > 2 && intersection((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) {
|
||||
while (polyline.points.size() > 2 && intersection_ln((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) {
|
||||
polyline.points.erase(polyline.points.begin() + 1);
|
||||
}
|
||||
}
|
||||
if (!grown_env.contains(to)) {
|
||||
while (polyline.points.size() > 2 && intersection((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) {
|
||||
while (polyline.points.size() > 2 && intersection_ln((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) {
|
||||
polyline.points.erase(polyline.points.end() - 2);
|
||||
}
|
||||
}
|
||||
|
@ -294,7 +294,7 @@ MotionPlannerEnv::nearest_env_point(const Point &from, const Point &to) const
|
|||
size_t result = from.nearest_waypoint_index(pp, to);
|
||||
|
||||
// as we assume 'from' is outside env, any node will require at least one crossing
|
||||
if (intersection((Lines)Line(from, pp[result]), this->island).size() > 1) {
|
||||
if (intersection_ln((Lines)Line(from, pp[result]), this->island).size() > 1) {
|
||||
// discard result
|
||||
pp.erase(pp.begin() + result);
|
||||
} else {
|
||||
|
|
|
@ -54,8 +54,7 @@ PerimeterGenerator::process()
|
|||
for (Surfaces::const_iterator surface = this->slices->surfaces.begin();
|
||||
surface != this->slices->surfaces.end(); ++surface) {
|
||||
// detect how many perimeters must be generated for this island
|
||||
signed short loop_number = this->config->perimeters + surface->extra_perimeters;
|
||||
loop_number--; // 0-indexed loops
|
||||
const int loop_number = this->config->perimeters + surface->extra_perimeters -1; // 0-indexed loops
|
||||
|
||||
Polygons gaps;
|
||||
|
||||
|
@ -67,7 +66,7 @@ PerimeterGenerator::process()
|
|||
ThickPolylines thin_walls;
|
||||
|
||||
// we loop one time more than needed in order to find gaps after the last perimeter was applied
|
||||
for (signed short i = 0; i <= loop_number+1; ++i) { // outer loop is 0
|
||||
for (int i = 0; i <= loop_number+1; ++i) { // outer loop is 0
|
||||
Polygons offsets;
|
||||
if (i == 0) {
|
||||
// the minimum thickness of a single loop is:
|
||||
|
@ -170,16 +169,16 @@ PerimeterGenerator::process()
|
|||
}
|
||||
|
||||
// nest loops: holes first
|
||||
for (signed short d = 0; d <= loop_number; ++d) {
|
||||
for (int d = 0; d <= loop_number; ++d) {
|
||||
PerimeterGeneratorLoops &holes_d = holes[d];
|
||||
|
||||
// loop through all holes having depth == d
|
||||
for (signed short i = 0; i < holes_d.size(); ++i) {
|
||||
for (int i = 0; i < (int)holes_d.size(); ++i) {
|
||||
const PerimeterGeneratorLoop &loop = holes_d[i];
|
||||
|
||||
// find the hole loop that contains this one, if any
|
||||
for (signed short t = d+1; t <= loop_number; ++t) {
|
||||
for (signed short j = 0; j < holes[t].size(); ++j) {
|
||||
for (int t = d+1; t <= loop_number; ++t) {
|
||||
for (int j = 0; j < (int)holes[t].size(); ++j) {
|
||||
PerimeterGeneratorLoop &candidate_parent = holes[t][j];
|
||||
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
|
||||
candidate_parent.children.push_back(loop);
|
||||
|
@ -191,8 +190,8 @@ PerimeterGenerator::process()
|
|||
}
|
||||
|
||||
// if no hole contains this hole, find the contour loop that contains it
|
||||
for (signed short t = loop_number; t >= 0; --t) {
|
||||
for (signed short j = 0; j < contours[t].size(); ++j) {
|
||||
for (int t = loop_number; t >= 0; --t) {
|
||||
for (int j = 0; j < (int)contours[t].size(); ++j) {
|
||||
PerimeterGeneratorLoop &candidate_parent = contours[t][j];
|
||||
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
|
||||
candidate_parent.children.push_back(loop);
|
||||
|
@ -207,16 +206,16 @@ PerimeterGenerator::process()
|
|||
}
|
||||
|
||||
// nest contour loops
|
||||
for (signed short d = loop_number; d >= 1; --d) {
|
||||
for (int d = loop_number; d >= 1; --d) {
|
||||
PerimeterGeneratorLoops &contours_d = contours[d];
|
||||
|
||||
// loop through all contours having depth == d
|
||||
for (signed short i = 0; i < contours_d.size(); ++i) {
|
||||
for (int i = 0; i < (int)contours_d.size(); ++i) {
|
||||
const PerimeterGeneratorLoop &loop = contours_d[i];
|
||||
|
||||
// find the contour loop that contains it
|
||||
for (signed short t = d-1; t >= 0; --t) {
|
||||
for (signed short j = 0; j < contours[t].size(); ++j) {
|
||||
for (int t = d-1; t >= 0; --t) {
|
||||
for (int j = 0; j < contours[t].size(); ++j) {
|
||||
PerimeterGeneratorLoop &candidate_parent = contours[t][j];
|
||||
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
|
||||
candidate_parent.children.push_back(loop);
|
||||
|
@ -315,8 +314,7 @@ PerimeterGenerator::process()
|
|||
coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE);
|
||||
|
||||
// append infill areas to fill_surfaces
|
||||
surfaces_append(
|
||||
this->fill_surfaces->surfaces,
|
||||
this->fill_surfaces->append(
|
||||
offset2_ex(
|
||||
pp,
|
||||
-inset -min_perimeter_infill_spacing/2,
|
||||
|
@ -354,36 +352,24 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,
|
|||
if (this->config->overhangs && this->layer_id > 0
|
||||
&& !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) {
|
||||
// get non-overhang paths by intersecting this loop with the grown lower slices
|
||||
{
|
||||
Polylines polylines;
|
||||
intersection((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
|
||||
|
||||
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
|
||||
ExtrusionPath path(role);
|
||||
path.polyline = *polyline;
|
||||
path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm;
|
||||
path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width;
|
||||
path.height = this->layer_height;
|
||||
paths.push_back(path);
|
||||
}
|
||||
}
|
||||
extrusion_paths_append(
|
||||
paths,
|
||||
intersection_pl(loop->polygon, this->_lower_slices_p),
|
||||
role,
|
||||
is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm,
|
||||
is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width,
|
||||
this->layer_height);
|
||||
|
||||
// get overhang paths by checking what parts of this loop fall
|
||||
// outside the grown lower slices (thus where the distance between
|
||||
// the loop centerline and original lower slices is >= half nozzle diameter
|
||||
{
|
||||
Polylines polylines;
|
||||
diff((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
|
||||
|
||||
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
|
||||
ExtrusionPath path(erOverhangPerimeter);
|
||||
path.polyline = *polyline;
|
||||
path.mm3_per_mm = this->_mm3_per_mm_overhang;
|
||||
path.width = this->overhang_flow.width;
|
||||
path.height = this->overhang_flow.height;
|
||||
paths.push_back(path);
|
||||
}
|
||||
}
|
||||
extrusion_paths_append(
|
||||
paths,
|
||||
diff_pl(loop->polygon, this->_lower_slices_p),
|
||||
erOverhangPerimeter,
|
||||
this->_mm3_per_mm_overhang,
|
||||
this->overhang_flow.width,
|
||||
this->overhang_flow.height);
|
||||
|
||||
// reapply the nearest point search for starting point
|
||||
// We allow polyline reversal because Clipper may have randomly
|
||||
|
@ -459,7 +445,7 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo
|
|||
ExtrusionPath path(role);
|
||||
ThickLines lines = p->thicklines();
|
||||
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
for (int i = 0; i < (int)lines.size(); ++i) {
|
||||
const ThickLine& line = lines[i];
|
||||
|
||||
const coordf_t line_len = line.length();
|
||||
|
|
|
@ -12,12 +12,6 @@ Point::Point(double x, double y)
|
|||
this->y = lrint(y);
|
||||
}
|
||||
|
||||
bool
|
||||
Point::operator==(const Point& rhs) const
|
||||
{
|
||||
return this->coincides_with(rhs);
|
||||
}
|
||||
|
||||
std::string
|
||||
Point::wkt() const
|
||||
{
|
||||
|
|
|
@ -36,7 +36,7 @@ class Point
|
|||
static Point new_scale(coordf_t x, coordf_t y) {
|
||||
return Point(scale_(x), scale_(y));
|
||||
};
|
||||
bool operator==(const Point& rhs) const;
|
||||
bool operator==(const Point& rhs) const { return this->x == rhs.x && this->y == rhs.y; }
|
||||
std::string wkt() const;
|
||||
std::string dump_perl() const;
|
||||
void scale(double factor);
|
||||
|
@ -70,6 +70,12 @@ inline Point operator+(const Point& point1, const Point& point2) { return Point(
|
|||
inline Point operator-(const Point& point1, const Point& point2) { return Point(point1.x - point2.x, point1.y - point2.y); }
|
||||
inline Point operator*(double scalar, const Point& point2) { return Point(scalar * point2.x, scalar * point2.y); }
|
||||
|
||||
struct PointHash {
|
||||
size_t operator()(const Point &pt) const {
|
||||
return std::hash<coord_t>()(pt.x) ^ std::hash<coord_t>()(pt.y);
|
||||
}
|
||||
};
|
||||
|
||||
class Point3 : public Point
|
||||
{
|
||||
public:
|
||||
|
@ -105,6 +111,9 @@ class Pointf
|
|||
inline Pointf operator+(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x + point2.x, point1.y + point2.y); }
|
||||
inline Pointf operator-(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x - point2.x, point1.y - point2.y); }
|
||||
inline Pointf operator*(double scalar, const Pointf& point2) { return Pointf(scalar * point2.x, scalar * point2.y); }
|
||||
inline Pointf operator*(const Pointf& point2, double scalar) { return Pointf(scalar * point2.x, scalar * point2.y); }
|
||||
inline coordf_t cross(const Pointf &v1, const Pointf &v2) { return v1.x * v2.y - v1.y * v2.x; }
|
||||
inline coordf_t dot(const Pointf &v1, const Pointf &v2) { return v1.x * v1.y + v2.x * v2.y; }
|
||||
|
||||
class Pointf3 : public Pointf
|
||||
{
|
||||
|
@ -122,7 +131,7 @@ class Pointf3 : public Pointf
|
|||
Vectorf3 vector_to(const Pointf3 &point) const;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Slic3r
|
||||
|
||||
// start Boost
|
||||
#include <boost/version.hpp>
|
||||
|
@ -146,28 +155,28 @@ namespace boost { namespace polygon {
|
|||
#endif
|
||||
|
||||
template <>
|
||||
struct geometry_concept<Point> { typedef point_concept type; };
|
||||
struct geometry_concept<Slic3r::Point> { typedef point_concept type; };
|
||||
|
||||
template <>
|
||||
struct point_traits<Point> {
|
||||
struct point_traits<Slic3r::Point> {
|
||||
typedef coord_t coordinate_type;
|
||||
|
||||
static inline coordinate_type get(const Point& point, orientation_2d orient) {
|
||||
static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) {
|
||||
return (orient == HORIZONTAL) ? point.x : point.y;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct point_mutable_traits<Point> {
|
||||
struct point_mutable_traits<Slic3r::Point> {
|
||||
typedef coord_t coordinate_type;
|
||||
static inline void set(Point& point, orientation_2d orient, coord_t value) {
|
||||
static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) {
|
||||
if (orient == HORIZONTAL)
|
||||
point.x = value;
|
||||
else
|
||||
point.y = value;
|
||||
}
|
||||
static inline Point construct(coord_t x_value, coord_t y_value) {
|
||||
Point retval;
|
||||
static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) {
|
||||
Slic3r::Point retval;
|
||||
retval.x = x_value;
|
||||
retval.y = y_value;
|
||||
return retval;
|
||||
|
|
|
@ -112,9 +112,7 @@ double Polygon::area() const
|
|||
bool
|
||||
Polygon::is_counter_clockwise() const
|
||||
{
|
||||
ClipperLib::Path p;
|
||||
Slic3rMultiPoint_to_ClipperPath(*this, &p);
|
||||
return ClipperLib::Orientation(p);
|
||||
return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this));
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -190,8 +188,7 @@ Polygon::simplify(double tolerance) const
|
|||
|
||||
Polygons pp;
|
||||
pp.push_back(p);
|
||||
simplify_polygons(pp, &pp);
|
||||
return pp;
|
||||
return simplify_polygons(pp);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -142,43 +142,43 @@ inline Polylines to_polylines(Polygons &&polys)
|
|||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
template <>
|
||||
struct geometry_concept<Polygon>{ typedef polygon_concept type; };
|
||||
struct geometry_concept<Slic3r::Polygon>{ typedef polygon_concept type; };
|
||||
|
||||
template <>
|
||||
struct polygon_traits<Polygon> {
|
||||
struct polygon_traits<Slic3r::Polygon> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef Points::const_iterator iterator_type;
|
||||
typedef Point point_type;
|
||||
typedef Slic3r::Points::const_iterator iterator_type;
|
||||
typedef Slic3r::Point point_type;
|
||||
|
||||
// Get the begin iterator
|
||||
static inline iterator_type begin_points(const Polygon& t) {
|
||||
static inline iterator_type begin_points(const Slic3r::Polygon& t) {
|
||||
return t.points.begin();
|
||||
}
|
||||
|
||||
// Get the end iterator
|
||||
static inline iterator_type end_points(const Polygon& t) {
|
||||
static inline iterator_type end_points(const Slic3r::Polygon& t) {
|
||||
return t.points.end();
|
||||
}
|
||||
|
||||
// Get the number of sides of the polygon
|
||||
static inline std::size_t size(const Polygon& t) {
|
||||
static inline std::size_t size(const Slic3r::Polygon& t) {
|
||||
return t.points.size();
|
||||
}
|
||||
|
||||
// Get the winding direction of the polygon
|
||||
static inline winding_direction winding(const Polygon& t) {
|
||||
static inline winding_direction winding(const Slic3r::Polygon& t) {
|
||||
return unknown_winding;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_mutable_traits<Polygon> {
|
||||
struct polygon_mutable_traits<Slic3r::Polygon> {
|
||||
// expects stl style iterators
|
||||
template <typename iT>
|
||||
static inline Polygon& set_points(Polygon& polygon, iT input_begin, iT input_end) {
|
||||
static inline Slic3r::Polygon& set_points(Slic3r::Polygon& polygon, iT input_begin, iT input_end) {
|
||||
polygon.points.clear();
|
||||
while (input_begin != input_end) {
|
||||
polygon.points.push_back(Point());
|
||||
polygon.points.push_back(Slic3r::Point());
|
||||
boost::polygon::assign(polygon.points.back(), *input_begin);
|
||||
++input_begin;
|
||||
}
|
||||
|
@ -189,32 +189,32 @@ namespace boost { namespace polygon {
|
|||
};
|
||||
|
||||
template <>
|
||||
struct geometry_concept<Polygons> { typedef polygon_set_concept type; };
|
||||
struct geometry_concept<Slic3r::Polygons> { typedef polygon_set_concept type; };
|
||||
|
||||
//next we map to the concept through traits
|
||||
template <>
|
||||
struct polygon_set_traits<Polygons> {
|
||||
struct polygon_set_traits<Slic3r::Polygons> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef Polygons::const_iterator iterator_type;
|
||||
typedef Polygons operator_arg_type;
|
||||
typedef Slic3r::Polygons::const_iterator iterator_type;
|
||||
typedef Slic3r::Polygons operator_arg_type;
|
||||
|
||||
static inline iterator_type begin(const Polygons& polygon_set) {
|
||||
static inline iterator_type begin(const Slic3r::Polygons& polygon_set) {
|
||||
return polygon_set.begin();
|
||||
}
|
||||
|
||||
static inline iterator_type end(const Polygons& polygon_set) {
|
||||
static inline iterator_type end(const Slic3r::Polygons& polygon_set) {
|
||||
return polygon_set.end();
|
||||
}
|
||||
|
||||
//don't worry about these, just return false from them
|
||||
static inline bool clean(const Polygons& polygon_set) { return false; }
|
||||
static inline bool sorted(const Polygons& polygon_set) { return false; }
|
||||
static inline bool clean(const Slic3r::Polygons& polygon_set) { return false; }
|
||||
static inline bool sorted(const Slic3r::Polygons& polygon_set) { return false; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_set_mutable_traits<Polygons> {
|
||||
struct polygon_set_mutable_traits<Slic3r::Polygons> {
|
||||
template <typename input_iterator_type>
|
||||
static inline void set(Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) {
|
||||
static inline void set(Slic3r::Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) {
|
||||
polygons.assign(input_begin, input_end);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -608,20 +608,15 @@ Print::validate() const
|
|||
object->model_object()->instances.front()->transform_polygon(&convex_hull);
|
||||
|
||||
// grow convex hull with the clearance margin
|
||||
{
|
||||
Polygons grown_hull;
|
||||
offset(convex_hull, &grown_hull, scale_(this->config.extruder_clearance_radius.value)/2, 1, jtRound, scale_(0.1));
|
||||
convex_hull = grown_hull.front();
|
||||
}
|
||||
convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front();
|
||||
|
||||
// now we check that no instance of convex_hull intersects any of the previously checked object instances
|
||||
for (Points::const_iterator copy = object->_shifted_copies.begin(); copy != object->_shifted_copies.end(); ++copy) {
|
||||
Polygon p = convex_hull;
|
||||
p.translate(*copy);
|
||||
if (intersects(a, p))
|
||||
if (! intersection(a, p).empty())
|
||||
return "Some objects are too close; your extruder will collide with them.";
|
||||
|
||||
union_(a, p, &a);
|
||||
polygons_append(a, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "Layer.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "PlaceholderParser.hpp"
|
||||
|
||||
#include "Slicing.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -78,6 +78,10 @@ public:
|
|||
std::map< size_t,std::vector<int> > region_volumes;
|
||||
PrintObjectConfig config;
|
||||
t_layer_height_ranges layer_height_ranges;
|
||||
|
||||
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
|
||||
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
|
||||
std::vector<coordf_t> layer_height_profile;
|
||||
|
||||
// this is set to true when LayerRegion->slices is split in top/internal/bottom
|
||||
// so that next call to make_perimeters() performs a union() before computing loops
|
||||
|
@ -136,13 +140,26 @@ public:
|
|||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
bool invalidate_step(PrintObjectStep step);
|
||||
bool invalidate_all_steps();
|
||||
|
||||
|
||||
// Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile.
|
||||
// The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces.
|
||||
void update_layer_height_profile();
|
||||
|
||||
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
|
||||
// by the interactive layer height editor and by the printing process itself.
|
||||
// The slicing parameters are dependent on various configuration values
|
||||
// (layer height, first layer height, raft settings, print nozzle diameter etc).
|
||||
SlicingParameters slicing_parameters() const;
|
||||
|
||||
void _slice();
|
||||
bool has_support_material() const;
|
||||
void detect_surfaces_type();
|
||||
void process_external_surfaces();
|
||||
void discover_vertical_shells();
|
||||
void bridge_over_infill();
|
||||
|
||||
void _make_perimeters();
|
||||
void _infill();
|
||||
|
||||
private:
|
||||
Print* _print;
|
||||
ModelObject* _model_object;
|
||||
|
@ -152,6 +169,8 @@ private:
|
|||
// parameter
|
||||
PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox);
|
||||
~PrintObject() {}
|
||||
|
||||
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
|
||||
};
|
||||
|
||||
typedef std::vector<PrintObject*> PrintObjectPtrs;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "PrintConfig.hpp"
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -1290,9 +1291,11 @@ PrintConfigDef::PrintConfigDef()
|
|||
def->cli = "threads|j=i";
|
||||
def->readonly = true;
|
||||
def->min = 1;
|
||||
def->max = 16;
|
||||
def->default_value = new ConfigOptionInt(2);
|
||||
|
||||
{
|
||||
unsigned int threads = boost::thread::hardware_concurrency();
|
||||
def->default_value = new ConfigOptionInt(threads > 0 ? threads : 2);
|
||||
}
|
||||
|
||||
def = this->add("toolchange_gcode", coString);
|
||||
def->label = "Tool change G-code";
|
||||
def->tooltip = "This custom code is inserted right before every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder].";
|
||||
|
|
|
@ -2,10 +2,23 @@
|
|||
#include "BoundingBox.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "SVG.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#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 {
|
||||
|
||||
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.
|
||||
void PrintObject::detect_surfaces_type()
|
||||
{
|
||||
// Slic3r::debugf "Detecting solid surfaces...\n";
|
||||
BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces...";
|
||||
|
||||
for (int idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) {
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
for (int idx_layer = 0; idx_layer < int(this->layer_count()); ++ idx_layer) {
|
||||
|
@ -435,7 +449,7 @@ void PrintObject::detect_surfaces_type()
|
|||
{
|
||||
Polygons topbottom = to_polygons(top);
|
||||
polygons_append(topbottom, to_polygons(bottom));
|
||||
surfaces_append(layerm->slices.surfaces,
|
||||
layerm->slices.append(
|
||||
#if 0
|
||||
offset2_ex(diff(layerm_slices_surfaces, topbottom, true), -offset, offset),
|
||||
#else
|
||||
|
@ -444,8 +458,8 @@ void PrintObject::detect_surfaces_type()
|
|||
stInternal);
|
||||
}
|
||||
|
||||
surfaces_append(layerm->slices.surfaces, STDMOVE(top));
|
||||
surfaces_append(layerm->slices.surfaces, STDMOVE(bottom));
|
||||
layerm->slices.append(STDMOVE(top));
|
||||
layerm->slices.append(STDMOVE(bottom));
|
||||
|
||||
// Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n",
|
||||
// $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
|
||||
|
@ -469,6 +483,8 @@ void PrintObject::detect_surfaces_type()
|
|||
void
|
||||
PrintObject::process_external_surfaces()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Processing external surfaces...";
|
||||
|
||||
FOREACH_REGION(this->_print, region) {
|
||||
size_t region_id = region - this->_print->regions.begin();
|
||||
|
||||
|
@ -497,6 +513,8 @@ PrintObject::discover_vertical_shells()
|
|||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells...";
|
||||
|
||||
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
|
||||
|
||||
for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) {
|
||||
|
@ -692,8 +710,7 @@ PrintObject::discover_vertical_shells()
|
|||
#if 1
|
||||
// Intentionally inflate a bit more than how much the region has been shrunk,
|
||||
// so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
|
||||
shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing,
|
||||
CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare);
|
||||
shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare);
|
||||
if (shell.empty())
|
||||
continue;
|
||||
#else
|
||||
|
@ -705,7 +722,7 @@ PrintObject::discover_vertical_shells()
|
|||
// get a triangle in $too_narrow; if we grow it below then the shell
|
||||
// would have a different shape from the external surface and we'd still
|
||||
// have the same angle, so the next shell would be grown even more and so on.
|
||||
Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 5.), true);
|
||||
Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, ClipperLib::jtMiter, 5.), true);
|
||||
if (! too_narrow.empty()) {
|
||||
// grow the collapsing parts and add the extra area to the neighbor layer
|
||||
// as well as to our original surfaces so that we support this
|
||||
|
@ -754,9 +771,9 @@ PrintObject::discover_vertical_shells()
|
|||
// Assign resulting internal surfaces to layer.
|
||||
const SurfaceType surfaceTypesKeep[] = { stTop, stBottom, stBottomBridge };
|
||||
layerm->fill_surfaces.keep_types(surfaceTypesKeep, sizeof(surfaceTypesKeep)/sizeof(SurfaceType));
|
||||
layerm->fill_surfaces.append(stInternal , new_internal);
|
||||
layerm->fill_surfaces.append(stInternalVoid , new_internal_void);
|
||||
layerm->fill_surfaces.append(stInternalSolid, new_internal_solid);
|
||||
layerm->fill_surfaces.append(new_internal, stInternal);
|
||||
layerm->fill_surfaces.append(new_internal_void, stInternalVoid);
|
||||
layerm->fill_surfaces.append(new_internal_solid, stInternalSolid);
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells");
|
||||
|
@ -775,6 +792,8 @@ PrintObject::discover_vertical_shells()
|
|||
void
|
||||
PrintObject::bridge_over_infill()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Bridge over infill...";
|
||||
|
||||
FOREACH_REGION(this->_print, region) {
|
||||
size_t region_id = region - this->_print->regions.begin();
|
||||
|
||||
|
@ -846,7 +865,7 @@ PrintObject::bridge_over_infill()
|
|||
#endif
|
||||
|
||||
// compute the remaning internal solid surfaces as difference
|
||||
ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true);
|
||||
ExPolygons not_to_bridge = diff_ex(internal_solid, to_polygons(to_bridge), true);
|
||||
to_bridge = intersection_ex(to_polygons(to_bridge), internal_solid, true);
|
||||
|
||||
// build the new collection of fill_surfaces
|
||||
|
@ -902,4 +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 ®ion = **region_it;
|
||||
|
||||
|
||||
if (!region.config.extra_perimeters
|
||||
|| region.config.perimeters == 0
|
||||
|| region.config.fill_density == 0
|
||||
|| this->layer_count() < 2) continue;
|
||||
|
||||
for (int i = 0; i < int(this->layer_count()) - 1; ++i) {
|
||||
LayerRegion &layerm = *this->get_layer(i)->get_region(region_id);
|
||||
const LayerRegion &upper_layerm = *this->get_layer(i+1)->get_region(region_id);
|
||||
const Polygons upper_layerm_polygons = upper_layerm.slices;
|
||||
|
||||
// Filter upper layer polygons in intersection_ppl by their bounding boxes?
|
||||
// my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
|
||||
double total_loop_length = 0;
|
||||
for (Polygons::const_iterator it = upper_layerm_polygons.begin(); it != upper_layerm_polygons.end(); ++it)
|
||||
total_loop_length += it->length();
|
||||
|
||||
const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing();
|
||||
const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter);
|
||||
const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width();
|
||||
const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing();
|
||||
|
||||
for (Surfaces::iterator slice = layerm.slices.surfaces.begin();
|
||||
slice != layerm.slices.surfaces.end(); ++slice) {
|
||||
while (true) {
|
||||
// compute the total thickness of perimeters
|
||||
const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
|
||||
+ (region.config.perimeters-1 + region.config.extra_perimeters) * perimeter_spacing;
|
||||
|
||||
// define a critical area where we don't want the upper slice to fall into
|
||||
// (it should either lay over our perimeters or outside this area)
|
||||
const coord_t critical_area_depth = perimeter_spacing * 1.5;
|
||||
const Polygons critical_area = diff(
|
||||
offset(slice->expolygon, -perimeters_thickness),
|
||||
offset(slice->expolygon, -(perimeters_thickness + critical_area_depth))
|
||||
);
|
||||
|
||||
// check whether a portion of the upper slices falls inside the critical area
|
||||
const Polylines intersection = intersection_pl(
|
||||
to_polylines(upper_layerm_polygons),
|
||||
critical_area
|
||||
);
|
||||
|
||||
// only add an additional loop if at least 30% of the slice loop would benefit from it
|
||||
{
|
||||
double total_intersection_length = 0;
|
||||
for (Polylines::const_iterator it = intersection.begin(); it != intersection.end(); ++it)
|
||||
total_intersection_length += it->length();
|
||||
if (total_intersection_length <= total_loop_length*0.3) break;
|
||||
}
|
||||
|
||||
/*
|
||||
if (0) {
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"extra.svg",
|
||||
no_arrows => 1,
|
||||
expolygons => union_ex($critical_area),
|
||||
polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
slice->extra_perimeters++;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (slice->extra_perimeters > 0)
|
||||
printf(" adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, i);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parallelize<Layer*>(
|
||||
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
|
||||
boost::bind(&Slic3r::Layer::make_perimeters, _1),
|
||||
this->_print->config.threads.value
|
||||
);
|
||||
|
||||
/*
|
||||
simplify slices (both layer and region slices),
|
||||
we only need the max resolution for perimeters
|
||||
### This makes this method not-idempotent, so we keep it disabled for now.
|
||||
###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
|
||||
*/
|
||||
|
||||
this->state.set_done(posPerimeters);
|
||||
}
|
||||
|
||||
void
|
||||
PrintObject::_infill()
|
||||
{
|
||||
if (this->state.is_done(posInfill)) return;
|
||||
this->state.set_started(posInfill);
|
||||
|
||||
parallelize<Layer*>(
|
||||
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
|
||||
boost::bind(&Slic3r::Layer::make_fills, _1),
|
||||
this->_print->config.threads.value
|
||||
);
|
||||
|
||||
/* we could free memory now, but this would make this step not idempotent
|
||||
### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
|
||||
*/
|
||||
|
||||
this->state.set_done(posInfill);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
585
xs/src/libslic3r/Slicing.cpp
Normal file
585
xs/src/libslic3r/Slicing.cpp
Normal 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
|
112
xs/src/libslic3r/Slicing.hpp
Normal file
112
xs/src/libslic3r/Slicing.hpp
Normal 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_ */
|
140
xs/src/libslic3r/SlicingAdaptive.cpp
Normal file
140
xs/src/libslic3r/SlicingAdaptive.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
#include "libslic3r.h"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "SlicingAdaptive.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
void SlicingAdaptive::clear()
|
||||
{
|
||||
m_meshes.clear();
|
||||
m_faces.clear();
|
||||
m_face_normal_z.clear();
|
||||
}
|
||||
|
||||
std::pair<float, float> face_z_span(const stl_facet *f)
|
||||
{
|
||||
return std::pair<float, float>(
|
||||
std::min(std::min(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z),
|
||||
std::max(std::max(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z));
|
||||
}
|
||||
|
||||
void SlicingAdaptive::prepare()
|
||||
{
|
||||
// 1) Collect faces of all meshes.
|
||||
int nfaces_total = 0;
|
||||
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
|
||||
nfaces_total += (*it_mesh)->stl.stats.number_of_facets;
|
||||
m_faces.reserve(nfaces_total);
|
||||
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
|
||||
for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i)
|
||||
m_faces.push_back((*it_mesh)->stl.facet_start + i);
|
||||
|
||||
// 2) Sort faces lexicographically by their Z span.
|
||||
std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) {
|
||||
std::pair<float, float> span1 = face_z_span(f1);
|
||||
std::pair<float, float> span2 = face_z_span(f2);
|
||||
return span1 < span2;
|
||||
});
|
||||
|
||||
// 3) Generate Z components of the facet normals.
|
||||
m_face_normal_z.assign(m_faces.size(), 0.f);
|
||||
for (size_t iface = 0; iface < m_faces.size(); ++ iface)
|
||||
m_face_normal_z[iface] = m_faces[iface]->normal.z;
|
||||
}
|
||||
|
||||
float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet)
|
||||
{
|
||||
float height = m_slicing_params.max_layer_height;
|
||||
bool first_hit = false;
|
||||
|
||||
// find all facets intersecting the slice-layer
|
||||
int ordered_id = current_facet;
|
||||
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
|
||||
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
|
||||
// facet's minimum is higher than slice_z -> end loop
|
||||
if (zspan.first >= z)
|
||||
break;
|
||||
// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
|
||||
if (zspan.second > z) {
|
||||
// first event?
|
||||
if (! first_hit) {
|
||||
first_hit = true;
|
||||
current_facet = ordered_id;
|
||||
}
|
||||
// skip touching facets which could otherwise cause small cusp values
|
||||
if (zspan.second <= z + EPSILON)
|
||||
continue;
|
||||
// compute cusp-height for this facet and store minimum of all heights
|
||||
float normal_z = m_face_normal_z[ordered_id];
|
||||
height = std::min(height, (normal_z == 0.f) ? 9999.f : std::abs(cusp_value / normal_z));
|
||||
}
|
||||
}
|
||||
|
||||
// lower height limit due to printer capabilities
|
||||
height = std::max(height, float(m_slicing_params.min_layer_height));
|
||||
|
||||
// check for sloped facets inside the determined layer and correct height if necessary
|
||||
if (height > m_slicing_params.min_layer_height) {
|
||||
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
|
||||
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
|
||||
// facet's minimum is higher than slice_z + height -> end loop
|
||||
if (zspan.first >= z + height)
|
||||
break;
|
||||
|
||||
// skip touching facets which could otherwise cause small cusp values
|
||||
if (zspan.second <= z + EPSILON)
|
||||
continue;
|
||||
|
||||
// Compute cusp-height for this facet and check against height.
|
||||
float normal_z = m_face_normal_z[ordered_id];
|
||||
float cusp = (normal_z == 0) ? 9999 : abs(cusp_value / normal_z);
|
||||
|
||||
float z_diff = zspan.first - z;
|
||||
|
||||
// handle horizontal facets
|
||||
if (m_face_normal_z[ordered_id] > 0.999) {
|
||||
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
|
||||
height = z_diff;
|
||||
// Slic3r::debugf "to %f due to near horizontal facet\n", $height;
|
||||
} else if (cusp > z_diff) {
|
||||
if (cusp < height) {
|
||||
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
|
||||
height = cusp;
|
||||
// Slic3r::debugf "to %f due to new cusp height\n", $height;
|
||||
}
|
||||
} else {
|
||||
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
|
||||
height = z_diff;
|
||||
// Slic3r::debugf "to z-diff: %f\n", $height;
|
||||
}
|
||||
}
|
||||
// lower height limit due to printer capabilities again
|
||||
height = std::max(height, float(m_slicing_params.min_layer_height));
|
||||
}
|
||||
|
||||
// Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height;
|
||||
return height;
|
||||
}
|
||||
|
||||
// Returns the distance to the next horizontal facet in Z-dir
|
||||
// to consider horizontal object features in slice thickness
|
||||
float SlicingAdaptive::horizontal_facet_distance(float z)
|
||||
{
|
||||
for (size_t i = 0; i < m_faces.size(); ++ i) {
|
||||
std::pair<float, float> zspan = face_z_span(m_faces[i]);
|
||||
// facet's minimum is higher than max forward distance -> end loop
|
||||
if (zspan.first > z + m_slicing_params.max_layer_height)
|
||||
break;
|
||||
// min_z == max_z -> horizontal facet
|
||||
if (zspan.first > z && zspan.first == zspan.second)
|
||||
return zspan.first - z;
|
||||
}
|
||||
|
||||
// objects maximum?
|
||||
return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ?
|
||||
std::max<float>(m_slicing_params.object_print_z_height() - z, 0.f) :
|
||||
m_slicing_params.max_layer_height;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
36
xs/src/libslic3r/SlicingAdaptive.hpp
Normal file
36
xs/src/libslic3r/SlicingAdaptive.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Based on implementation by @platsch
|
||||
|
||||
#ifndef slic3r_SlicingAdaptive_hpp_
|
||||
#define slic3r_SlicingAdaptive_hpp_
|
||||
|
||||
#include "Slicing.hpp"
|
||||
#include "admesh/stl.h"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
class TriangleMesh;
|
||||
|
||||
class SlicingAdaptive
|
||||
{
|
||||
public:
|
||||
void clear();
|
||||
void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; }
|
||||
void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); }
|
||||
void prepare();
|
||||
float cusp_height(float z, float cusp_value, int ¤t_facet);
|
||||
float horizontal_facet_distance(float z);
|
||||
|
||||
protected:
|
||||
SlicingParameters m_slicing_params;
|
||||
|
||||
std::vector<const TriangleMesh*> m_meshes;
|
||||
// Collected faces of all meshes, sorted by raising Z of the bottom most face.
|
||||
std::vector<const stl_facet*> m_faces;
|
||||
// Z component of face normals, normalized.
|
||||
std::vector<float> m_face_normal_z;
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_SlicingAdaptive_hpp_ */
|
|
@ -5,11 +5,12 @@
|
|||
#include "Print.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
#include "Fill/FillBase.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <unordered_set>
|
||||
|
||||
// #define SLIC3R_DEBUG
|
||||
|
||||
|
@ -19,6 +20,8 @@
|
|||
#include "SVG.hpp"
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// 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.
|
||||
// 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);
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
for (MyLayersPtr::const_iterator it = bottom_contacts.begin(); it != bottom_contacts.end(); ++ it) {
|
||||
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 */
|
||||
std::vector<Polygons> layer_support_areas;
|
||||
MyLayersPtr bottom_contacts = this->bottom_contact_layers_and_layer_support_areas(
|
||||
object, top_contacts, layer_storage,
|
||||
layer_support_areas);
|
||||
|
||||
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";
|
||||
|
||||
// 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
|
||||
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.
|
||||
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
|
||||
static int iRun = 0;
|
||||
|
@ -577,7 +575,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
// workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
|
||||
for (Polylines::iterator it = overhang_perimeters.begin(); it != overhang_perimeters.end(); ++ it)
|
||||
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 overhangs having endpoints inside layer's slices
|
||||
|
@ -590,13 +588,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
if (it->is_straight()) {
|
||||
it->extend_start(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.
|
||||
Polylines tmp; tmp.push_back(*it);
|
||||
Polygons out;
|
||||
offset(tmp, &out, 0.5f * w + 10.f);
|
||||
polygons_append(bridged_perimeters, out);
|
||||
}
|
||||
polygons_append(bridged_perimeters, offset(*it, 0.5f * w + 10.f));
|
||||
}
|
||||
}
|
||||
bridged_perimeters = union_(bridged_perimeters);
|
||||
|
@ -613,13 +607,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
|
||||
Polygons unsupported_bridge_polygons;
|
||||
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.
|
||||
Polylines tmp; tmp.push_back(*it);
|
||||
Polygons out;
|
||||
offset(tmp, &out, scale_(SUPPORT_MATERIAL_MARGIN));
|
||||
polygons_append(unsupported_bridge_polygons, out);
|
||||
}
|
||||
polygons_append(unsupported_bridge_polygons, offset(*it, scale_(SUPPORT_MATERIAL_MARGIN)));
|
||||
polygons_append(diff_polygons, intersection(unsupported_bridge_polygons, bridges));
|
||||
} else {
|
||||
// just remove bridged areas
|
||||
|
@ -664,10 +654,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
offset(
|
||||
diff_polygons,
|
||||
SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS,
|
||||
CLIPPER_OFFSET_SCALE,
|
||||
ClipperLib::jtRound,
|
||||
// round mitter limit
|
||||
scale_(0.05) * CLIPPER_OFFSET_SCALE),
|
||||
scale_(0.05)),
|
||||
slices_margin);
|
||||
}
|
||||
}
|
||||
|
@ -753,14 +742,188 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
|||
return contact_out;
|
||||
}
|
||||
|
||||
PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers(
|
||||
const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const
|
||||
#if 0
|
||||
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
|
||||
// we'll use them to clip our support and detect where does it stick
|
||||
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.
|
||||
Polygons projection;
|
||||
// Last top contact layer visited when collecting the projection of contact areas.
|
||||
|
@ -768,56 +931,116 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
|
|||
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;
|
||||
const Layer &layer = *object.get_layer(layer_id);
|
||||
Polygons top = collect_region_slices_by_type(layer, stTop);
|
||||
if (top.empty())
|
||||
continue;
|
||||
// Top surfaces of this layer, to be used to stop the surface volume from growing down.
|
||||
Polygons top;
|
||||
if (! m_object_config->support_material_buildplate_only)
|
||||
top = collect_region_slices_by_type(layer, stTop);
|
||||
size_t projection_size_old = projection.size();
|
||||
// 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) {
|
||||
Polygons polygons_new;
|
||||
// Contact surfaces are expanded away from the object, trimmed by the object.
|
||||
// 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.
|
||||
// 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())
|
||||
continue;
|
||||
if (projection_size_old < projection.size()) {
|
||||
// Merge the newly added regions. Don't use the safety offset, the offset has been added already.
|
||||
projection = union_(projection, false);
|
||||
#if 0
|
||||
union_and_fillet(projection, projection_size_old);
|
||||
#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
|
||||
// top surfaces above layer.print_z falls onto this top surface.
|
||||
// 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.
|
||||
Polygons touching = intersection(top, projection, false);
|
||||
if (touching.empty())
|
||||
continue;
|
||||
// Allocate a new bottom contact layer.
|
||||
MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact);
|
||||
bottom_contacts.push_back(&layer_new);
|
||||
// Grow top surfaces so that interface and support generation are generated
|
||||
// with some spacing from object - it looks we don't need the actual
|
||||
// top shapes so this can be done here
|
||||
layer_new.height = m_soluble_interface ?
|
||||
// Align the interface layer with the object's layer height.
|
||||
object.get_layer(layer_id + 1)->height :
|
||||
// Place a bridge flow interface layer over the top surface.
|
||||
m_support_material_interface_flow.nozzle_diameter;
|
||||
layer_new.print_z = layer.print_z + layer_new.height +
|
||||
(m_soluble_interface ? 0. : m_object_config->support_material_contact_distance.value);
|
||||
layer_new.bottom_z = layer.print_z;
|
||||
layer_new.idx_object_layer_below = layer_id;
|
||||
layer_new.bridging = ! m_soluble_interface;
|
||||
//FIXME how much to inflate the top surface?
|
||||
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.
|
||||
projection = diff(projection, touching);
|
||||
}
|
||||
}
|
||||
if (! top.empty()) {
|
||||
Polygons touching = intersection(top, projection, false);
|
||||
if (! touching.empty()) {
|
||||
// Allocate a new bottom contact layer.
|
||||
MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact);
|
||||
bottom_contacts.push_back(&layer_new);
|
||||
// Grow top surfaces so that interface and support generation are generated
|
||||
// with some spacing from object - it looks we don't need the actual
|
||||
// top shapes so this can be done here
|
||||
layer_new.height = m_soluble_interface ?
|
||||
// Align the interface layer with the object's layer height.
|
||||
object.get_layer(layer_id + 1)->height :
|
||||
// Place a bridge flow interface layer over the top surface.
|
||||
m_support_material_interface_flow.nozzle_diameter;
|
||||
layer_new.print_z = layer.print_z + layer_new.height +
|
||||
(m_soluble_interface ? 0. : m_object_config->support_material_contact_distance.value);
|
||||
layer_new.bottom_z = layer.print_z;
|
||||
layer_new.idx_object_layer_below = layer_id;
|
||||
layer_new.bridging = ! m_soluble_interface;
|
||||
//FIXME how much to inflate the top surface?
|
||||
layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()));
|
||||
#ifdef SLIC3R_DEBUG
|
||||
{
|
||||
::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());
|
||||
} // ! top_contacts.empty()
|
||||
|
||||
std::reverse(bottom_contacts.begin(), bottom_contacts.end());
|
||||
return bottom_contacts;
|
||||
}
|
||||
|
||||
|
@ -952,7 +1175,8 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
|||
const PrintObject &object,
|
||||
const MyLayersPtr &bottom_contacts,
|
||||
const MyLayersPtr &top_contacts,
|
||||
MyLayersPtr &intermediate_layers) const
|
||||
MyLayersPtr &intermediate_layers,
|
||||
std::vector<Polygons> &layer_support_areas) const
|
||||
{
|
||||
#ifdef SLIC3R_DEBUG
|
||||
static int iRun = 0;
|
||||
|
@ -965,25 +1189,50 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
|||
// coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
|
||||
int idx_top_contact_above = int(top_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)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - creating layer " <<
|
||||
idx_intermediate << " of " << intermediate_layers.size();
|
||||
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.
|
||||
while (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->bottom_z > layer_intermediate.print_z + EPSILON)
|
||||
-- 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.
|
||||
if (idx_intermediate + 1 < int(intermediate_layers.size()))
|
||||
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 polygons_trimming;
|
||||
Polygons polygons_trimming;
|
||||
|
||||
// Find the first top_contact layer intersecting with this layer.
|
||||
int idx_top_contact_overlapping = idx_top_contact_above;
|
||||
|
@ -1004,7 +1253,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
|||
-- idx_bottom_contact_overlapping;
|
||||
// Collect all the top_contact layer intersecting with this layer.
|
||||
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)
|
||||
break;
|
||||
polygons_append(polygons_trimming, layer_bottom_overlapping.polygons);
|
||||
|
@ -1038,9 +1287,8 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
|||
$fillet_radius_scaled,
|
||||
-$fillet_radius_scaled,
|
||||
# Use a geometric offsetting for filleting.
|
||||
CLIPPER_OFFSET_SCALE,
|
||||
JT_ROUND,
|
||||
0.2*$fillet_radius_scaled*CLIPPER_OFFSET_SCALE),
|
||||
0.2*$fillet_radius_scaled),
|
||||
$trim_polygons,
|
||||
false); // don't apply the safety offset.
|
||||
}
|
||||
|
@ -1063,6 +1311,9 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
|||
size_t idx_object_layer_overlapping = 0;
|
||||
// For all intermediate support layers:
|
||||
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);
|
||||
if (layer_intermediate.polygons.empty())
|
||||
// Empty support layer, nothing to trim.
|
||||
|
@ -1337,10 +1588,8 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
|
|||
// Positions of the loop centers.
|
||||
Polygons circles;
|
||||
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) {
|
||||
Polylines tmp;
|
||||
tmp.push_back(it_contact->split_at_first_point());
|
||||
if (! intersection(tmp, overhang_with_margin).empty()) {
|
||||
for (Polygons::const_iterator it_contact = top_contact_polygons.begin(); it_contact != top_contact_polygons.end(); ++ it_contact)
|
||||
if (! intersection_pl(it_contact->split_at_first_point(), overhang_with_margin).empty()) {
|
||||
external_loops.push_back(*it_contact);
|
||||
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) {
|
||||
|
@ -1350,7 +1599,6 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
|
|||
circle_new.points[i].translate(*it_center);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Apply a pattern to the loop.
|
||||
loops0 = diff(external_loops, circles);
|
||||
}
|
||||
|
@ -1369,7 +1617,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
|
|||
loop_lines.reserve(loop_polygons.size());
|
||||
for (Polygons::const_iterator it = loop_polygons.begin(); it != loop_polygons.end(); ++ it)
|
||||
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
|
||||
|
@ -1377,9 +1625,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
|
|||
// extrusions are left inside the circles; however it creates
|
||||
// a very large gap between loops and contact_infill_polygons, so maybe another
|
||||
// solution should be found to achieve both goals
|
||||
Polygons thick_loop_lines;
|
||||
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));
|
||||
top_contact_layer.layer->polygons = diff(top_contact_layer.layer->polygons, offset(loop_lines, float(circle_radius * 1.1)));
|
||||
|
||||
// Transform loops into ExtrusionPath objects.
|
||||
extrusion_entities_append_paths(
|
||||
|
@ -1598,7 +1844,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
|||
// TODO: use brim ordering algorithm
|
||||
Polygons to_infill_polygons = to_polygons(to_infill);
|
||||
// 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(
|
||||
support_layer.support_fills.entities,
|
||||
to_polylines(STDMOVE(to_infill_polygons)),
|
||||
|
|
|
@ -163,7 +163,9 @@ private:
|
|||
// Generate bottom contact layers supporting the top contact layers.
|
||||
// For a soluble interface material synchronize the layer heights with the object,
|
||||
// otherwise set the layer height to a bridging flow of a support interface nozzle.
|
||||
MyLayersPtr bottom_contact_layers(const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const;
|
||||
MyLayersPtr bottom_contact_layers_and_layer_support_areas(
|
||||
const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage,
|
||||
std::vector<Polygons> &layer_support_areas) const;
|
||||
|
||||
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
|
||||
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const;
|
||||
|
@ -180,7 +182,8 @@ private:
|
|||
const PrintObject &object,
|
||||
const MyLayersPtr &bottom_contacts,
|
||||
const MyLayersPtr &top_contacts,
|
||||
MyLayersPtr &intermediate_layers) const;
|
||||
MyLayersPtr &intermediate_layers,
|
||||
std::vector<Polygons> &layer_support_areas) const;
|
||||
|
||||
Polygons generate_raft_base(
|
||||
const PrintObject &object,
|
||||
|
|
|
@ -196,19 +196,6 @@ SurfaceCollection::remove_types(const SurfaceType *types, int ntypes)
|
|||
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
||||
}
|
||||
|
||||
void
|
||||
SurfaceCollection::append(const SurfaceCollection &coll)
|
||||
{
|
||||
this->surfaces.insert(this->surfaces.end(), coll.surfaces.begin(), coll.surfaces.end());
|
||||
}
|
||||
|
||||
void
|
||||
SurfaceCollection::append(const SurfaceType surfaceType, const Slic3r::ExPolygons &expoly)
|
||||
{
|
||||
for (Slic3r::ExPolygons::const_iterator it = expoly.begin(); it != expoly.end(); ++ it)
|
||||
this->surfaces.push_back(Slic3r::Surface(surfaceType, *it));
|
||||
}
|
||||
|
||||
void SurfaceCollection::export_to_svg(const char *path, bool show_labels)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
|
|
|
@ -28,8 +28,27 @@ class SurfaceCollection
|
|||
void remove_type(const SurfaceType type);
|
||||
void remove_types(const SurfaceType *types, int ntypes);
|
||||
void filter_by_type(SurfaceType type, Polygons* polygons);
|
||||
void append(const SurfaceCollection &coll);
|
||||
void append(const SurfaceType surfaceType, const ExPolygons &expoly);
|
||||
|
||||
void clear() { surfaces.clear(); }
|
||||
bool empty() const { return surfaces.empty(); }
|
||||
|
||||
void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
|
||||
void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
|
||||
void set(const ExPolygons &src, SurfaceType surfaceType) { clear(); this->append(src, surfaceType); }
|
||||
void set(const ExPolygons &src, const Surface &surfaceTempl) { clear(); this->append(src, surfaceTempl); }
|
||||
void set(const Surfaces &src) { clear(); this->append(src); }
|
||||
void set(ExPolygons &&src, SurfaceType surfaceType) { clear(); this->append(std::move(src), surfaceType); }
|
||||
void set(ExPolygons &&src, const Surface &surfaceTempl) { clear(); this->append(std::move(src), surfaceTempl); }
|
||||
void set(Surfaces &&src) { clear(); this->append(std::move(src)); }
|
||||
|
||||
void append(const SurfaceCollection &coll) { this->append(coll.surfaces); }
|
||||
void append(SurfaceCollection &&coll) { this->append(std::move(coll.surfaces)); }
|
||||
void append(const ExPolygons &src, SurfaceType surfaceType) { surfaces_append(this->surfaces, src, surfaceType); }
|
||||
void append(const ExPolygons &src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, src, surfaceTempl); }
|
||||
void append(const Surfaces &src) { surfaces_append(this->surfaces, src); }
|
||||
void append(ExPolygons &&src, SurfaceType surfaceType) { surfaces_append(this->surfaces, std::move(src), surfaceType); }
|
||||
void append(ExPolygons &&src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, std::move(src), surfaceTempl); }
|
||||
void append(Surfaces &&src) { surfaces_append(this->surfaces, std::move(src)); }
|
||||
|
||||
// For debugging purposes:
|
||||
void export_to_svg(const char *path, bool show_labels);
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#include "ClipperUtils.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include <cmath>
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
@ -193,12 +193,17 @@ void TriangleMesh::scale(const Pointf3 &versor)
|
|||
|
||||
void TriangleMesh::translate(float x, float y, float z)
|
||||
{
|
||||
if (x == 0.f && y == 0.f && z == 0.f)
|
||||
return;
|
||||
stl_translate_relative(&(this->stl), x, y, z);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::rotate(float angle, const Axis &axis)
|
||||
{
|
||||
if (angle == 0.f)
|
||||
return;
|
||||
|
||||
// admesh uses degrees
|
||||
angle = Slic3r::Geometry::rad2deg(angle);
|
||||
|
||||
|
@ -265,6 +270,8 @@ void TriangleMesh::align_to_origin()
|
|||
|
||||
void TriangleMesh::rotate(double angle, Point* center)
|
||||
{
|
||||
if (angle == 0.)
|
||||
return;
|
||||
this->translate(-center->x, -center->y, 0);
|
||||
stl_rotate_z(&(this->stl), (float)angle);
|
||||
this->translate(+center->x, +center->y, 0);
|
||||
|
@ -363,10 +370,7 @@ TriangleMesh::horizontal_projection() const
|
|||
}
|
||||
|
||||
// the offset factor was tuned using groovemount.stl
|
||||
offset(pp, &pp, 0.01 / SCALING_FACTOR);
|
||||
ExPolygons retval;
|
||||
union_(pp, &retval, true);
|
||||
return retval;
|
||||
return union_ex(offset(pp, 0.01 / SCALING_FACTOR), true);
|
||||
}
|
||||
|
||||
Polygon
|
||||
|
@ -403,7 +407,7 @@ TriangleMesh::require_shared_vertices()
|
|||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers)
|
||||
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
|
||||
{
|
||||
/*
|
||||
This method gets called with a list of unscaled Z coordinates and outputs
|
||||
|
@ -427,58 +431,66 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
|
|||
|
||||
At the end, we free the tables generated by analyze() as we don't
|
||||
need them anymore.
|
||||
FUTURE: parallelize slice_facet() and make_loops()
|
||||
|
||||
NOTE: this method accepts a vector of floats because the mesh coordinate
|
||||
type is float.
|
||||
*/
|
||||
|
||||
std::vector<IntersectionLines> lines(z.size());
|
||||
|
||||
for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) {
|
||||
stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
|
||||
|
||||
// find facet extents
|
||||
float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z));
|
||||
float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z));
|
||||
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
|
||||
facet->vertex[0].x, facet->vertex[0].y, facet->vertex[0].z,
|
||||
facet->vertex[1].x, facet->vertex[1].y, facet->vertex[1].z,
|
||||
facet->vertex[2].x, facet->vertex[2].y, facet->vertex[2].z);
|
||||
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
|
||||
#endif
|
||||
|
||||
// find layer extents
|
||||
std::vector<float>::const_iterator min_layer, max_layer;
|
||||
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
|
||||
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
|
||||
#endif
|
||||
|
||||
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
|
||||
std::vector<float>::size_type layer_idx = it - z.begin();
|
||||
this->slice_facet(*it / SCALING_FACTOR, *facet, facet_idx, min_z, max_z, &lines[layer_idx]);
|
||||
}
|
||||
{
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
// 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]);
|
||||
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
|
||||
const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z));
|
||||
const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z));
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
|
||||
facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z,
|
||||
facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z,
|
||||
facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z);
|
||||
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
|
||||
#endif
|
||||
|
||||
// find layer extents
|
||||
std::vector<float>::const_iterator min_layer, max_layer;
|
||||
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
|
||||
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
|
||||
#endif
|
||||
|
||||
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
|
||||
std::vector<float>::size_type layer_idx = it - z.begin();
|
||||
this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers)
|
||||
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
|
||||
{
|
||||
std::vector<Polygons> layers_p;
|
||||
this->slice(z, &layers_p);
|
||||
|
@ -495,7 +507,9 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>*
|
|||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines) const
|
||||
TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx,
|
||||
const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines,
|
||||
boost::mutex* lines_mutex) const
|
||||
{
|
||||
std::vector<IntersectionPoint> points;
|
||||
std::vector< std::vector<IntersectionPoint>::size_type > points_on_layer;
|
||||
|
@ -547,7 +561,12 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
|
|||
line.b.y = b->y;
|
||||
line.a_id = a_id;
|
||||
line.b_id = b_id;
|
||||
lines->push_back(line);
|
||||
if (lines_mutex != NULL) {
|
||||
boost::lock_guard<boost::mutex> l(*lines_mutex);
|
||||
lines->push_back(line);
|
||||
} else {
|
||||
lines->push_back(line);
|
||||
}
|
||||
|
||||
found_horizontal_edge = true;
|
||||
|
||||
|
@ -600,13 +619,24 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
|
|||
line.b_id = points[0].point_id;
|
||||
line.edge_a_id = points[1].edge_id;
|
||||
line.edge_b_id = points[0].edge_id;
|
||||
lines->push_back(line);
|
||||
if (lines_mutex != NULL) {
|
||||
boost::lock_guard<boost::mutex> l(*lines_mutex);
|
||||
lines->push_back(line);
|
||||
} else {
|
||||
lines->push_back(line);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
|
||||
TriangleMeshSlicer::_make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const
|
||||
{
|
||||
this->make_loops((*lines)[i], &(*layers)[i]);
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
|
||||
{
|
||||
/*
|
||||
SVG svg("lines.svg");
|
||||
|
@ -707,6 +737,7 @@ TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* l
|
|||
for (IntersectionLinePtrs::const_iterator lineptr = loop.begin(); lineptr != loop.end(); ++lineptr) {
|
||||
p.points.push_back((*lineptr)->a);
|
||||
}
|
||||
|
||||
loops->push_back(p);
|
||||
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
|
@ -746,7 +777,7 @@ class _area_comp {
|
|||
};
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices)
|
||||
TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
|
||||
{
|
||||
Polygons loops;
|
||||
this->make_loops(lines, &loops);
|
||||
|
@ -780,7 +811,7 @@ TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines,
|
|||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
|
||||
TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const
|
||||
{
|
||||
/*
|
||||
Input loops are not suitable for evenodd nor nonzero fill types, as we might get
|
||||
|
@ -818,17 +849,15 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
|
|||
of the loops, since the Orientation() function provided by Clipper
|
||||
would do the same, thus repeating the calculation */
|
||||
Polygons::const_iterator loop = loops.begin() + *loop_idx;
|
||||
if (area[*loop_idx] > +EPSILON) {
|
||||
if (area[*loop_idx] > +EPSILON)
|
||||
p_slices.push_back(*loop);
|
||||
} else if (area[*loop_idx] < -EPSILON) {
|
||||
diff(p_slices, *loop, &p_slices);
|
||||
}
|
||||
else if (area[*loop_idx] < -EPSILON)
|
||||
p_slices = diff(p_slices, *loop);
|
||||
}
|
||||
|
||||
// perform a safety offset to merge very close facets (TODO: find test case for this)
|
||||
double safety_offset = scale_(0.0499);
|
||||
ExPolygons ex_slices;
|
||||
offset2(p_slices, &ex_slices, +safety_offset, -safety_offset);
|
||||
ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset);
|
||||
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
size_t holes_count = 0;
|
||||
|
@ -840,11 +869,11 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
|
|||
#endif
|
||||
|
||||
// append to the supplied collection
|
||||
slices->insert(slices->end(), ex_slices.begin(), ex_slices.end());
|
||||
expolygons_append(*slices, ex_slices);
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices)
|
||||
TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
|
||||
{
|
||||
Polygons pp;
|
||||
this->make_loops(lines, &pp);
|
||||
|
@ -852,7 +881,7 @@ TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPoly
|
|||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
|
||||
TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const
|
||||
{
|
||||
IntersectionLines upper_lines, lower_lines;
|
||||
|
||||
|
@ -1004,7 +1033,6 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
|
|||
|
||||
stl_get_size(&(upper->stl));
|
||||
stl_get_size(&(lower->stl));
|
||||
|
||||
}
|
||||
|
||||
TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "libslic3r.h"
|
||||
#include <admesh/stl.h>
|
||||
#include <vector>
|
||||
#include <boost/thread.hpp>
|
||||
#include "BoundingBox.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "Point.hpp"
|
||||
|
@ -88,19 +89,23 @@ class TriangleMeshSlicer
|
|||
TriangleMesh* mesh;
|
||||
TriangleMeshSlicer(TriangleMesh* _mesh);
|
||||
~TriangleMeshSlicer();
|
||||
void slice(const std::vector<float> &z, std::vector<Polygons>* layers);
|
||||
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers);
|
||||
void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines) const;
|
||||
void cut(float z, TriangleMesh* upper, TriangleMesh* lower);
|
||||
void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const;
|
||||
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const;
|
||||
void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx,
|
||||
const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines,
|
||||
boost::mutex* lines_mutex = NULL) const;
|
||||
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
|
||||
|
||||
private:
|
||||
typedef std::vector< std::vector<int> > t_facets_edges;
|
||||
t_facets_edges facets_edges;
|
||||
stl_vertex* v_scaled_shared;
|
||||
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops);
|
||||
void make_expolygons(const Polygons &loops, ExPolygons* slices);
|
||||
void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices);
|
||||
void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices);
|
||||
void _slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, const std::vector<float> &z) const;
|
||||
void _make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const;
|
||||
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const;
|
||||
void make_expolygons(const Polygons &loops, ExPolygons* slices) const;
|
||||
void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
|
||||
void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -4,10 +4,14 @@
|
|||
// this needs to be included early for MSVC (listing it in Build.PL is not enough)
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
#include <cstdio>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <vector>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
|
||||
#define SLIC3R_VERSION "1.31.6"
|
||||
|
@ -40,13 +44,6 @@
|
|||
typedef long coord_t;
|
||||
typedef double coordf_t;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum Axis { X=0, Y, Z };
|
||||
|
||||
}
|
||||
using namespace Slic3r;
|
||||
|
||||
/* Implementation of CONFESS("foo"): */
|
||||
#ifdef _MSC_VER
|
||||
#define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
|
||||
|
@ -91,4 +88,55 @@ inline std::string debug_out_path(const char *name, ...)
|
|||
// Write slices as SVG images into out directory during the 2D processing of the slices.
|
||||
// #define SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum Axis { X=0, Y, Z };
|
||||
|
||||
template <class T>
|
||||
inline void append_to(std::vector<T> &dst, const std::vector<T> &src)
|
||||
{
|
||||
dst.insert(dst.end(), src.begin(), src.end());
|
||||
}
|
||||
|
||||
template <class T> void
|
||||
_parallelize_do(std::queue<T>* queue, boost::mutex* queue_mutex, boost::function<void(T)> func)
|
||||
{
|
||||
//std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl;
|
||||
while (true) {
|
||||
T i;
|
||||
{
|
||||
boost::lock_guard<boost::mutex> l(*queue_mutex);
|
||||
if (queue->empty()) return;
|
||||
i = queue->front();
|
||||
queue->pop();
|
||||
}
|
||||
//std::cout << " Thread " << boost::this_thread::get_id() << " processing item " << i << std::endl;
|
||||
func(i);
|
||||
boost::this_thread::interruption_point();
|
||||
}
|
||||
}
|
||||
|
||||
template <class T> void
|
||||
parallelize(std::queue<T> queue, boost::function<void(T)> func,
|
||||
int threads_count = boost::thread::hardware_concurrency())
|
||||
{
|
||||
if (threads_count == 0) threads_count = 2;
|
||||
boost::mutex queue_mutex;
|
||||
boost::thread_group workers;
|
||||
for (int i = 0; i < 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
|
||||
|
|
|
@ -1,3 +1,30 @@
|
|||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
static boost::log::trivial::severity_level logSeverity = boost::log::trivial::fatal;
|
||||
|
||||
void set_logging_level(unsigned int level)
|
||||
{
|
||||
switch (level) {
|
||||
case 0: logSeverity = boost::log::trivial::fatal; break;
|
||||
case 1: logSeverity = boost::log::trivial::error; break;
|
||||
case 2: logSeverity = boost::log::trivial::warning; break;
|
||||
case 3: logSeverity = boost::log::trivial::info; break;
|
||||
case 4: logSeverity = boost::log::trivial::debug; break;
|
||||
default: logSeverity = boost::log::trivial::trace; break;
|
||||
}
|
||||
|
||||
boost::log::core::get()->set_filter
|
||||
(
|
||||
boost::log::trivial::severity >= logSeverity
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#ifdef SLIC3R_HAS_BROKEN_CROAK
|
||||
|
||||
// Some Strawberry Perl builds (mainly the latest 64bit builds) have a broken mechanism
|
||||
|
@ -66,30 +93,3 @@ confess_at(const char *file, int line, const char *func,
|
|||
}
|
||||
|
||||
#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
|
||||
|
|
|
@ -532,8 +532,7 @@ SV*
|
|||
polynode2perl(const ClipperLib::PolyNode& node)
|
||||
{
|
||||
HV* hv = newHV();
|
||||
Slic3r::Polygon p;
|
||||
ClipperPath_to_Slic3rMultiPoint(node.Contour, &p);
|
||||
Slic3r::Polygon p = ClipperPath_to_Slic3rPolygon(node.Contour);
|
||||
if (node.IsHole()) {
|
||||
(void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) );
|
||||
} else {
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <ostream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <libslic3r.h>
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
extern "C" {
|
||||
|
@ -42,15 +43,18 @@ extern "C" {
|
|||
#undef do_close
|
||||
#undef bind
|
||||
#undef seed
|
||||
#undef push
|
||||
#undef pop
|
||||
#ifdef _MSC_VER
|
||||
// Undef some of the macros set by Perl <xsinit.h>, which cause compilation errors on Win32
|
||||
#undef send
|
||||
#undef connect
|
||||
#undef seek
|
||||
#undef send
|
||||
#undef write
|
||||
#endif /* _MSC_VER */
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <libslic3r.h>
|
||||
#include <ClipperUtils.hpp>
|
||||
#include <Config.hpp>
|
||||
#include <ExPolygon.hpp>
|
||||
|
@ -163,4 +167,6 @@ SV* polynode2perl(const ClipperLib::PolyNode& node);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,7 +5,7 @@ use warnings;
|
|||
|
||||
use List::Util qw(sum);
|
||||
use Slic3r::XS;
|
||||
use Test::More tests => 23;
|
||||
use Test::More tests => 16;
|
||||
|
||||
my $square = Slic3r::Polygon->new( # ccw
|
||||
[200, 100],
|
||||
|
@ -121,41 +121,6 @@ if (0) { # Clipper does not preserve polyline orientation
|
|||
is_deeply $result->[0]->pp, [[200,150], [100,150]], 'clipped line orientation is preserved';
|
||||
}
|
||||
|
||||
if (0) { # Clipper does not preserve polyline orientation
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$hole_in_square], [$square]);
|
||||
is_deeply $result->[0]->pp, $hole_in_square->split_at_first_point->pp,
|
||||
'intersection_ppl - clipping cw polygon as polyline preserves winding order';
|
||||
}
|
||||
|
||||
{
|
||||
my $square2 = $square->clone;
|
||||
$square2->translate(50,50);
|
||||
{
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
|
||||
is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
|
||||
is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
|
||||
# Clipper does not preserve polyline orientation so we only check the middle point
|
||||
###ok $result->[0][0]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
|
||||
ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
|
||||
###ok $result->[0][2]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
my $square2 = $square->clone;
|
||||
$square2->reverse;
|
||||
$square2->translate(50,50);
|
||||
{
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
|
||||
is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
|
||||
is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
|
||||
# Clipper does not preserve polyline orientation so we only check the middle point
|
||||
###ok $result->[0][0]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
|
||||
ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
|
||||
###ok $result->[0][2]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
# Clipper bug #96 (our issue #2028)
|
||||
my $subject = Slic3r::Polyline->new(
|
||||
|
@ -168,17 +133,6 @@ if (0) { # Clipper does not preserve polyline orientation
|
|||
is scalar(@$result), 1, 'intersection_pl - result is not empty';
|
||||
}
|
||||
|
||||
{
|
||||
my $subject = Slic3r::Polygon->new(
|
||||
[44730000,31936670],[55270000,31936670],[55270000,25270000],[74730000,25270000],[74730000,44730000],[68063296,44730000],[68063296,55270000],[74730000,55270000],[74730000,74730000],[55270000,74730000],[55270000,68063296],[44730000,68063296],[44730000,74730000],[25270000,74730000],[25270000,55270000],[31936670,55270000],[31936670,44730000],[25270000,44730000],[25270000,25270000],[44730000,25270000]
|
||||
);
|
||||
my $clip = [
|
||||
Slic3r::Polygon->new([75200000,45200000],[54800000,45200000],[54800000,24800000],[75200000,24800000]),
|
||||
];
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$subject], $clip);
|
||||
is scalar(@$result), 1, 'intersection_ppl - result is not empty';
|
||||
}
|
||||
|
||||
{
|
||||
# Clipper bug #122
|
||||
my $subject = [
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
long y_min() %code{% RETVAL = THIS->min.y; %};
|
||||
long y_max() %code{% RETVAL = THIS->max.y; %};
|
||||
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld;%ld,%ld", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %};
|
||||
|
||||
bool defined() %code{% RETVAL = THIS->defined; %};
|
||||
|
||||
%{
|
||||
|
||||
BoundingBox*
|
||||
|
@ -69,7 +70,8 @@ new_from_points(CLASS, points)
|
|||
void set_y_min(double val) %code{% THIS->min.y = val; %};
|
||||
void set_y_max(double val) %code{% THIS->max.y = val; %};
|
||||
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf;%lf,%lf", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %};
|
||||
|
||||
bool defined() %code{% RETVAL = THIS->defined; %};
|
||||
|
||||
%{
|
||||
|
||||
BoundingBoxf*
|
||||
|
@ -106,4 +108,5 @@ new_from_points(CLASS, points)
|
|||
double z_min() %code{% RETVAL = THIS->min.z; %};
|
||||
double z_max() %code{% RETVAL = THIS->max.z; %};
|
||||
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf;%lf,%lf,%lf", THIS->min.x, THIS->min.y, THIS->min.z, THIS->max.x, THIS->max.y, THIS->max.z); RETVAL = buf; %};
|
||||
bool defined() %code{% RETVAL = THIS->defined; %};
|
||||
};
|
||||
|
|
|
@ -16,58 +16,53 @@ _constant()
|
|||
JT_MITER = jtMiter
|
||||
JT_ROUND = jtRound
|
||||
JT_SQUARE = jtSquare
|
||||
CLIPPER_OFFSET_SCALE = CLIPPER_OFFSET_SCALE
|
||||
CODE:
|
||||
RETVAL = ix;
|
||||
OUTPUT: RETVAL
|
||||
|
||||
Polygons
|
||||
offset(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset(polygons, &RETVAL, delta, scale, joinType, miterLimit);
|
||||
RETVAL = offset(polygons, delta, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
ExPolygons
|
||||
offset_ex(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset_ex(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset(polygons, &RETVAL, delta, scale, joinType, miterLimit);
|
||||
RETVAL = offset_ex(polygons, delta, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Polygons
|
||||
offset2(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset2(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta1
|
||||
const float delta2
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit);
|
||||
RETVAL = offset2(polygons, delta1, delta2, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
ExPolygons
|
||||
offset2_ex(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset2_ex(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta1
|
||||
const float delta2
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit);
|
||||
RETVAL = offset2_ex(polygons, delta1, delta2, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -77,7 +72,7 @@ diff(subject, clip, safety_offset = false)
|
|||
Polygons clip
|
||||
bool safety_offset
|
||||
CODE:
|
||||
diff(subject, clip, &RETVAL, safety_offset);
|
||||
RETVAL = diff(subject, clip, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -87,7 +82,7 @@ diff_ex(subject, clip, safety_offset = false)
|
|||
Polygons clip
|
||||
bool safety_offset
|
||||
CODE:
|
||||
diff(subject, clip, &RETVAL, safety_offset);
|
||||
RETVAL = diff_ex(subject, clip, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -96,16 +91,7 @@ diff_pl(subject, clip)
|
|||
Polylines subject
|
||||
Polygons clip
|
||||
CODE:
|
||||
diff(subject, clip, &RETVAL);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Polylines
|
||||
diff_ppl(subject, clip)
|
||||
Polygons subject
|
||||
Polygons clip
|
||||
CODE:
|
||||
diff(subject, clip, &RETVAL);
|
||||
RETVAL = diff_pl(subject, clip);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -115,7 +101,7 @@ intersection(subject, clip, safety_offset = false)
|
|||
Polygons clip
|
||||
bool safety_offset
|
||||
CODE:
|
||||
intersection(subject, clip, &RETVAL, safety_offset);
|
||||
RETVAL = intersection(subject, clip, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -125,7 +111,7 @@ intersection_ex(subject, clip, safety_offset = false)
|
|||
Polygons clip
|
||||
bool safety_offset
|
||||
CODE:
|
||||
intersection(subject, clip, &RETVAL, safety_offset);
|
||||
RETVAL = intersection_ex(subject, clip, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -134,26 +120,7 @@ intersection_pl(subject, clip)
|
|||
Polylines subject
|
||||
Polygons clip
|
||||
CODE:
|
||||
intersection(subject, clip, &RETVAL);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Polylines
|
||||
intersection_ppl(subject, clip)
|
||||
Polygons subject
|
||||
Polygons clip
|
||||
CODE:
|
||||
intersection(subject, clip, &RETVAL);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
ExPolygons
|
||||
xor_ex(subject, clip, safety_offset = false)
|
||||
Polygons subject
|
||||
Polygons clip
|
||||
bool safety_offset
|
||||
CODE:
|
||||
xor_(subject, clip, &RETVAL, safety_offset);
|
||||
RETVAL = intersection_pl(subject, clip);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -162,7 +129,7 @@ union(subject, safety_offset = false)
|
|||
Polygons subject
|
||||
bool safety_offset
|
||||
CODE:
|
||||
union_(subject, &RETVAL, safety_offset);
|
||||
RETVAL = union_(subject, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -171,20 +138,7 @@ union_ex(subject, safety_offset = false)
|
|||
Polygons subject
|
||||
bool safety_offset
|
||||
CODE:
|
||||
union_(subject, &RETVAL, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
SV*
|
||||
union_pt(subject, safety_offset = false)
|
||||
Polygons subject
|
||||
bool safety_offset
|
||||
CODE:
|
||||
// perform operation
|
||||
ClipperLib::PolyTree polytree;
|
||||
union_pt(subject, &polytree, safety_offset);
|
||||
|
||||
RETVAL = polynode_children_2_perl(polytree);
|
||||
RETVAL = union_ex(subject, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -193,7 +147,7 @@ union_pt_chained(subject, safety_offset = false)
|
|||
Polygons subject
|
||||
bool safety_offset
|
||||
CODE:
|
||||
union_pt_chained(subject, &RETVAL, safety_offset);
|
||||
RETVAL = union_pt_chained(subject, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -201,7 +155,7 @@ Polygons
|
|||
simplify_polygons(subject)
|
||||
Polygons subject
|
||||
CODE:
|
||||
simplify_polygons(subject, &RETVAL);
|
||||
RETVAL = simplify_polygons(subject);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
|
|
@ -80,13 +80,12 @@ Polyline::rotate(angle, center_sv)
|
|||
THIS->rotate(angle, center);
|
||||
|
||||
Polygons
|
||||
Polyline::grow(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtSquare, miterLimit = 3)
|
||||
Polyline::grow(delta, joinType = ClipperLib::jtSquare, miterLimit = 3)
|
||||
const float delta
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit);
|
||||
RETVAL = offset(*THIS, delta, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/Slicing.hpp"
|
||||
#include "libslic3r/PlaceholderParser.hpp"
|
||||
%}
|
||||
|
||||
|
@ -58,6 +59,8 @@ _constant()
|
|||
Points copies();
|
||||
t_layer_height_ranges layer_height_ranges()
|
||||
%code%{ RETVAL = THIS->layer_height_ranges; %};
|
||||
std::vector<double> layer_height_profile()
|
||||
%code%{ RETVAL = THIS->layer_height_profile; %};
|
||||
Ref<Point3> size()
|
||||
%code%{ RETVAL = &THIS->size; %};
|
||||
Clone<BoundingBox> bounding_box();
|
||||
|
@ -82,6 +85,8 @@ _constant()
|
|||
bool reload_model_instances();
|
||||
void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges)
|
||||
%code%{ THIS->layer_height_ranges = layer_height_ranges; %};
|
||||
void set_layer_height_profile(std::vector<double> profile)
|
||||
%code%{ THIS->layer_height_profile = profile; %};
|
||||
|
||||
size_t total_layer_count();
|
||||
size_t layer_count();
|
||||
|
@ -106,11 +111,31 @@ _constant()
|
|||
%code%{ THIS->state.set_done(step); %};
|
||||
void set_step_started(PrintObjectStep step)
|
||||
%code%{ THIS->state.set_started(step); %};
|
||||
|
||||
|
||||
void _slice();
|
||||
void detect_surfaces_type();
|
||||
void process_external_surfaces();
|
||||
void discover_vertical_shells();
|
||||
void bridge_over_infill();
|
||||
void _make_perimeters();
|
||||
void _infill();
|
||||
|
||||
void 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()
|
||||
%code%{ RETVAL = (int)(intptr_t)THIS; %};
|
||||
|
|
|
@ -83,13 +83,12 @@ Surface::polygons()
|
|||
RETVAL
|
||||
|
||||
Surfaces
|
||||
Surface::offset(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Surface::offset(delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
const float delta
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit);
|
||||
surfaces_append(RETVAL, offset_ex(THIS->expolygon, delta, joinType, miterLimit), *THIS);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
|
Loading…
Reference in a new issue