Change char to int

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

View File

@ -18,6 +18,8 @@ sub debugf {
printf @_ if $debug;
}
our $loglevel = 0;
# load threads before Moo as required by it
our $have_threads;
BEGIN {
@ -104,6 +106,10 @@ my $pause_sema = Thread::Semaphore->new;
my $parallel_sema;
my $paused = 0;
# Set the logging level at the Slic3r XS module.
$Slic3r::loglevel = (defined($ENV{'SLIC3R_LOGLEVEL'}) && $ENV{'SLIC3R_LOGLEVEL'} =~ /^[1-9]/) ? $ENV{'SLIC3R_LOGLEVEL'} : 0;
set_logging_level($Slic3r::loglevel);
sub spawn_thread {
my ($cb) = @_;

View File

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

View File

@ -19,7 +19,8 @@ package Slic3r::GUI::3DScene::Base;
use strict;
use warnings;
use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
use Wx qw(:timer);
use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_TIMER);
# must load OpenGL *before* Wx::GLCanvas
use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
use base qw(Wx::GLCanvas Class::Accessor);
@ -53,10 +54,15 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
origin
_mouse_pos
_hover_volume_idx
_drag_volume_idx
_drag_start_pos
_drag_start_xy
_dragged
layer_editing_enabled
_layer_height_edited
_camera_type
_camera_target
_camera_distance
@ -82,7 +88,10 @@ use constant VIEW_BOTTOM => [0.0,180.0];
use constant VIEW_FRONT => [0.0,90.0];
use constant VIEW_REAR => [180.0,90.0];
#use constant GIMBALL_LOCK_THETA_MAX => 150;
use constant MANIPULATION_IDLE => 0;
use constant MANIPULATION_DRAGGING => 1;
use constant MANIPULATION_LAYER_HEIGHT => 2;
use constant GIMBALL_LOCK_THETA_MAX => 170;
# make OpenGL::Array thread-safe
@ -131,6 +140,15 @@ sub new {
$self->_camera_target(Slic3r::Pointf3->new(0,0,0));
$self->_camera_distance(0.);
# Size of a layer height texture, used by a shader to color map the object print layers.
$self->{layer_preview_z_texture_width} = 512;
$self->{layer_preview_z_texture_height} = 512;
$self->{layer_height_edit_band_width} = 2.;
$self->{layer_height_edit_strength} = 0.005;
$self->{layer_height_edit_last_object_id} = -1;
$self->{layer_height_edit_last_z} = 0.;
$self->{layer_height_edit_last_action} = 0;
$self->reset_objects;
EVT_PAINT($self, sub {
@ -144,88 +162,142 @@ sub new {
$self->Resize( $self->GetSizeWH );
$self->Refresh;
});
EVT_MOUSEWHEEL($self, sub {
my ($self, $e) = @_;
# Calculate the zoom delta and apply it to the current zoom factor
my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta();
$zoom = max(min($zoom, 4), -4);
$zoom /= 10;
$self->_zoom($self->_zoom / (1-$zoom));
# In order to zoom around the mouse point we need to translate
# the camera target
my $size = Slic3r::Pointf->new($self->GetSizeWH);
my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #-
$self->_camera_target->translate(
# ($pos - $size/2) represents the vector from the viewport center
# to the mouse point. By multiplying it by $zoom we get the new,
# transformed, length of such vector.
# Since we want that point to stay fixed, we move our camera target
# in the opposite direction by the delta of the length of such vector
# ($zoom - 1). We then scale everything by 1/$self->_zoom since
# $self->_camera_target is expressed in terms of model units.
-($pos->x - $size->x/2) * ($zoom) / $self->_zoom,
-($pos->y - $size->y/2) * ($zoom) / $self->_zoom,
0,
) if 0;
$self->on_viewport_changed->() if $self->on_viewport_changed;
$self->_dirty(1);
$self->Refresh;
});
EVT_MOUSEWHEEL($self, \&mouse_wheel_event);
EVT_MOUSE_EVENTS($self, \&mouse_event);
$self->{layer_height_edit_timer_id} = &Wx::NewId();
$self->{layer_height_edit_timer} = Wx::Timer->new($self, $self->{layer_height_edit_timer_id});
EVT_TIMER($self, $self->{layer_height_edit_timer_id}, sub {
my ($self, $event) = @_;
return if ! $self->_layer_height_edited;
return if $self->{layer_height_edit_last_object_id} == -1;
$self->_variable_layer_thickness_action(undef, 1);
});
return $self;
}
sub Destroy {
my ($self) = @_;
$self->{layer_height_edit_timer}->Stop;
$self->DestroyGL;
return $self->SUPER::Destroy;
}
sub _first_selected_object_id {
my ($self) = @_;
for my $i (0..$#{$self->volumes}) {
if ($self->volumes->[$i]->selected) {
return int($self->volumes->[$i]->select_group_id / 1000000);
}
}
return -1;
}
# Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen.
sub _variable_layer_thickness_bar_rect {
my ($self) = @_;
my ($cw, $ch) = $self->GetSizeWH;
my $bar_width = 70;
return ($cw - $bar_width, 0, $cw, $ch);
}
sub _variable_layer_thickness_bar_rect_mouse_inside {
my ($self, $mouse_evt) = @_;
my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect;
return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom;
}
sub _variable_layer_thickness_bar_mouse_cursor_z {
my ($self, $object_idx, $mouse_evt) = @_;
my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect;
return unscale($self->{print}->get_object($object_idx)->size->z) * ($bar_bottom - $mouse_evt->GetY - 1.) / ($bar_bottom - $bar_top);
}
sub _variable_layer_thickness_action {
my ($self, $mouse_event, $do_modification) = @_;
# A volume is selected. Test, whether hovering over a layer thickness bar.
if (defined($mouse_event)) {
$self->{layer_height_edit_last_z} = $self->_variable_layer_thickness_bar_mouse_cursor_z($self->{layer_height_edit_last_object_id}, $mouse_event);
$self->{layer_height_edit_last_action} = $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1);
}
if ($self->{layer_height_edit_last_object_id} != -1) {
$self->{print}->get_object($self->{layer_height_edit_last_object_id})->adjust_layer_height_profile(
$self->{layer_height_edit_last_z},
$self->{layer_height_edit_strength},
$self->{layer_height_edit_band_width},
$self->{layer_height_edit_last_action});
$self->{print}->get_object($self->{layer_height_edit_last_object_id})->generate_layer_height_texture(
$self->volumes->[$self->{layer_height_edit_last_object_id}]->layer_height_texture_data->ptr,
$self->{layer_preview_z_texture_height},
$self->{layer_preview_z_texture_width});
$self->Refresh;
# Automatic action on mouse down with the same coordinate.
$self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS);
}
}
sub mouse_event {
my ($self, $e) = @_;
my $pos = Slic3r::Pointf->new($e->GetPositionXY);
my $object_idx_selected = $self->{layer_height_edit_last_object_id} = ($self->layer_editing_enabled && $self->{print}) ? $self->_first_selected_object_id : -1;
if ($e->Entering && &Wx::wxMSW) {
# wxMSW needs focus in order to catch mouse wheel events
$self->SetFocus;
} elsif ($e->LeftDClick) {
$self->on_double_click->()
if $self->on_double_click;
if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) {
} elsif ($self->on_double_click) {
$self->on_double_click->();
}
} elsif ($e->LeftDown || $e->RightDown) {
# If user pressed left or right button we first check whether this happened
# on a volume or not.
my $volume_idx = $self->_hover_volume_idx // -1;
$self->_layer_height_edited(0);
if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) {
# A volume is selected and the mouse is hovering over a layer thickness bar.
# Start editing the layer height.
$self->_layer_height_edited(1);
$self->_variable_layer_thickness_action($e, 1);
} else {
# Select volume in this 3D canvas.
# Don't deselect a volume if layer editing is enabled. We want the object to stay selected
# during the scene manipulation.
if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) {
$self->deselect_volumes;
$self->select_volume($volume_idx);
# 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->Refresh;
}
# propagate event through callback
$self->on_select->($volume_idx)
if $self->on_select;
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};
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;
}
}
$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 +322,9 @@ sub mouse_event {
$self->_dragged(1);
$self->Refresh;
} elsif ($e->Dragging) {
if ($e->LeftIsDown) {
if ($self->_layer_height_edited && $object_idx_selected != -1) {
$self->_variable_layer_thickness_action($e, 0);
} elsif ($e->LeftIsDown) {
# if dragging over blank area with left button, rotate
if (defined $self->_drag_start_pos) {
my $orig = $self->_drag_start_pos;
@ -305,14 +379,61 @@ sub mouse_event {
$self->_drag_start_pos(undef);
$self->_drag_start_xy(undef);
$self->_dragged(undef);
$self->_layer_height_edited(undef);
$self->{layer_height_edit_timer}->Stop;
} elsif ($e->Moving) {
$self->_mouse_pos($pos);
$self->Refresh;
# Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor
# hovers over.
$self->Refresh if ($self->enable_picking);
} else {
$e->Skip();
}
}
sub mouse_wheel_event {
my ($self, $e) = @_;
if ($self->layer_editing_enabled && $self->{print}) {
my $object_idx_selected = $self->_first_selected_object_id;
if ($object_idx_selected != -1) {
# A volume is selected. Test, whether hovering over a layer thickness bar.
if ($self->_variable_layer_thickness_bar_rect_mouse_inside($e)) {
# Adjust the width of the selection.
$self->{layer_height_edit_band_width} = max(min($self->{layer_height_edit_band_width} * (1 + 0.1 * $e->GetWheelRotation() / $e->GetWheelDelta()), 10.), 1.5);
$self->Refresh;
return;
}
}
}
# Calculate the zoom delta and apply it to the current zoom factor
my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta();
$zoom = max(min($zoom, 4), -4);
$zoom /= 10;
$self->_zoom($self->_zoom / (1-$zoom));
# In order to zoom around the mouse point we need to translate
# the camera target
my $size = Slic3r::Pointf->new($self->GetSizeWH);
my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #-
$self->_camera_target->translate(
# ($pos - $size/2) represents the vector from the viewport center
# to the mouse point. By multiplying it by $zoom we get the new,
# transformed, length of such vector.
# Since we want that point to stay fixed, we move our camera target
# in the opposite direction by the delta of the length of such vector
# ($zoom - 1). We then scale everything by 1/$self->_zoom since
# $self->_camera_target is expressed in terms of model units.
-($pos->x - $size->x/2) * ($zoom) / $self->_zoom,
-($pos->y - $size->y/2) * ($zoom) / $self->_zoom,
0,
) if 0;
$self->on_viewport_changed->() if $self->on_viewport_changed;
$self->_dirty(1);
$self->Refresh;
}
# Reset selection.
sub reset_objects {
my ($self) = @_;
@ -721,6 +842,21 @@ sub InitGL {
return unless $self->GetContext;
$self->init(1);
my $shader;
$shader = $self->{shader} = new Slic3r::GUI::GLShader
if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1);
if ($self->{shader}) {
my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader);
print $info if $info;
($self->{layer_preview_z_texture_id}) = glGenTextures_p(1);
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
glBindTexture(GL_TEXTURE_2D, 0);
}
glClearColor(0, 0, 0, 1);
glColor3f(1, 0, 0);
glEnable(GL_DEPTH_TEST);
@ -762,6 +898,13 @@ sub InitGL {
glEnable(GL_MULTISAMPLE);
}
sub DestroyGL {
my $self = shift;
if ($self->init && $self->GetContext) {
delete $self->{shader};
}
}
sub Render {
my ($self, $dc) = @_;
@ -799,10 +942,14 @@ sub Render {
glLightfv_p(GL_LIGHT0, GL_SPECULAR, 0.2, 0.2, 0.2, 1);
glLightfv_p(GL_LIGHT0, GL_DIFFUSE, 0.5, 0.5, 0.5, 1);
# Head light
glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0);
if ($self->enable_picking) {
# Render the object for picking.
# FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing.
# Better to use software ray-casting on a bounding-box hierarchy.
glDisable(GL_MULTISAMPLE);
glDisable(GL_LIGHTING);
$self->draw_volumes(1);
glFlush();
@ -829,6 +976,7 @@ sub Render {
glFlush();
glFinish();
glEnable(GL_LIGHTING);
glEnable(GL_MULTISAMPLE);
}
# draw fixed background
@ -948,9 +1096,12 @@ sub Render {
glDisable(GL_BLEND);
}
glFlush();
$self->draw_active_object_annotations;
$self->SwapBuffers();
# Calling glFinish has a performance penalty, but it seems to fix some OpenGL driver hang-up with extremely large scenes.
glFinish();
}
sub draw_volumes {
@ -963,10 +1114,61 @@ sub draw_volumes {
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
# The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),
# where x, y is the window size divided by $self->_zoom.
my ($cw, $ch) = $self->GetSizeWH;
my $bar_width = 70;
my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom));
my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom));
my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition());
my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. :
($ch - $mouse_pos->y - 1.) / ($ch - 1);
foreach my $volume_idx (0..$#{$self->volumes}) {
my $volume = $self->volumes->[$volume_idx];
if ($fakecolor) {
my $shader_active = 0;
if ($self->layer_editing_enabled && ! $fakecolor && $volume->selected && $self->{shader} && $volume->{layer_height_texture_data} && $volume->{layer_height_texture_cells}) {
$self->{shader}->Enable;
my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row');
my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized');
my $z_cursor_id = $self->{shader}->Map('z_cursor');
my $z_cursor_band_width_id = $self->{shader}->Map('z_cursor_band_width');
die if ! defined($z_to_texture_row_id);
die if ! defined($z_texture_row_to_normalized_id);
die if ! defined($z_cursor_id);
die if ! defined($z_cursor_band_width_id);
my $ncells = $volume->{layer_height_texture_cells};
my $z_max = $volume->{bounding_box}->z_max;
glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max));
glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height});
glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative);
glUniform1fARB($z_cursor_band_width_id, $self->{layer_height_edit_band_width});
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LEVEL, 0);
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
if (1) {
glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
# glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
# glPixelStorei(GL_UNPACK_ROW_LENGTH, $self->{layer_preview_z_texture_width});
glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr);
glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4));
} else {
glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr);
glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width}/2, $self->{layer_preview_z_texture_height}/2,
0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr + $self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4);
}
# my $nlines = ceil($ncells / ($self->{layer_preview_z_texture_width} - 1));
$shader_active = 1;
} elsif ($fakecolor) {
# Object picking mode. Render the object with a color encoding the object index.
my $r = ($volume_idx & 0x000000FF) >> 0;
my $g = ($volume_idx & 0x0000FF00) >> 8;
@ -1017,16 +1219,39 @@ sub draw_volumes {
if ($qverts_begin < $qverts_end) {
glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr);
glNormalPointer_c(GL_FLOAT, 0, $volume->qverts->norms_ptr);
glDrawArrays(GL_QUADS, $qverts_begin / 3, ($qverts_end-$qverts_begin) / 3);
$qverts_begin /= 3;
$qverts_end /= 3;
my $nvertices = $qverts_end-$qverts_begin;
while ($nvertices > 0) {
my $nvertices_this = ($nvertices > 4096) ? 4096 : $nvertices;
glDrawArrays(GL_QUADS, $qverts_begin, $nvertices_this);
$qverts_begin += $nvertices_this;
$nvertices -= $nvertices_this;
}
}
if ($tverts_begin < $tverts_end) {
glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr);
glNormalPointer_c(GL_FLOAT, 0, $volume->tverts->norms_ptr);
glDrawArrays(GL_TRIANGLES, $tverts_begin / 3, ($tverts_end-$tverts_begin) / 3);
$tverts_begin /= 3;
$tverts_end /= 3;
my $nvertices = $tverts_end-$tverts_begin;
while ($nvertices > 0) {
my $nvertices_this = ($nvertices > 4095) ? 4095 : $nvertices;
glDrawArrays(GL_TRIANGLES, $tverts_begin, $nvertices_this);
$tverts_begin += $nvertices_this;
$nvertices -= $nvertices_this;
}
}
glVertexPointer_c(3, GL_FLOAT, 0, 0);
glNormalPointer_c(GL_FLOAT, 0, 0);
glPopMatrix();
if ($shader_active) {
glBindTexture(GL_TEXTURE_2D, 0);
$self->{shader}->Disable;
}
}
glDisableClientState(GL_NORMAL_ARRAY);
glDisable(GL_BLEND);
@ -1036,10 +1261,98 @@ sub draw_volumes {
glColor3f(0, 0, 0);
glVertexPointer_p(3, $self->cut_lines_vertices);
glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3);
glVertexPointer_c(3, GL_FLOAT, 0, 0);
}
glDisableClientState(GL_VERTEX_ARRAY);
}
sub draw_active_object_annotations {
# $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking.
my ($self) = @_;
return if (! $self->{shader} || ! $self->layer_editing_enabled);
my $volume;
foreach my $volume_idx (0..$#{$self->volumes}) {
my $v = $self->volumes->[$volume_idx];
if ($v->selected && $v->{layer_height_texture_data} && $v->{layer_height_texture_cells}) {
$volume = $v;
last;
}
}
return if (! $volume);
# The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),
# where x, y is the window size divided by $self->_zoom.
my ($cw, $ch) = $self->GetSizeWH;
my $bar_width = 70;
my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom));
my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom));
my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition());
my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. :
($ch - $mouse_pos->y - 1.) / ($ch - 1);
$self->{shader}->Enable;
my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row');
my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized');
my $z_cursor_id = $self->{shader}->Map('z_cursor');
my $ncells = $volume->{layer_height_texture_cells};
my $z_max = $volume->{bounding_box}->z_max;
glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max));
glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height});
glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative);
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr);
glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4));
# Render the color bar.
glDisable(GL_DEPTH_TEST);
# The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth),
# where x, y is the window size divided by $self->_zoom.
glPushMatrix();
glLoadIdentity();
# Paint the overlay.
glBegin(GL_QUADS);
glVertex3f($bar_left, $bar_bottom, 0);
glVertex3f($bar_right, $bar_bottom, 0);
glVertex3f($bar_right, $bar_top, $volume->{bounding_box}->z_max);
glVertex3f($bar_left, $bar_top, $volume->{bounding_box}->z_max);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
$self->{shader}->Disable;
# Paint the graph.
my $object_idx = int($volume->select_group_id / 1000000);
my $print_object = $self->{print}->get_object($object_idx);
my $max_z = unscale($print_object->size->z);
my $profile = $print_object->layer_height_profile;
my $layer_height = $print_object->config->get('layer_height');
# Baseline
glColor3f(0., 0., 0.);
glBegin(GL_LINE_STRIP);
glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45, $bar_bottom);
glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45, $bar_top);
glEnd();
# Curve
glColor3f(0., 0., 1.);
glBegin(GL_LINE_STRIP);
for (my $i = 0; $i < int(@{$profile}); $i += 2) {
my $z = $profile->[$i];
my $h = $profile->[$i+1];
glVertex3f($bar_left + $h * ($bar_right - $bar_left) / 0.45, $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z);
}
glEnd();
# Revert the matrices.
glPopMatrix();
glEnable(GL_DEPTH_TEST);
}
sub _report_opengl_state
{
my ($self, $comment) = @_;
@ -1069,6 +1382,112 @@ sub _report_opengl_state
}
}
sub _vertex_shader {
return <<'VERTEX';
#version 110
#define LIGHT_TOP_DIR 0., 1., 0.
#define LIGHT_TOP_DIFFUSE 0.2
#define LIGHT_TOP_SPECULAR 0.3
#define LIGHT_FRONT_DIR 0., 0., 1.
#define LIGHT_FRONT_DIFFUSE 0.5
#define LIGHT_FRONT_SPECULAR 0.3
#define INTENSITY_AMBIENT 0.1
uniform float z_to_texture_row;
varying float intensity_specular;
varying float intensity_tainted;
varying float object_z;
void main()
{
vec3 eye, normal, lightDir, viewVector, halfVector;
float NdotL, NdotHV;
// eye = gl_ModelViewMatrixInverse[3].xyz;
eye = vec3(0., 0., 1.);
// First transform the normal into eye space and normalize the result.
normal = normalize(gl_NormalMatrix * gl_Normal);
// Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space.
// Also since we're talking about a directional light, the position field is actually direction.
lightDir = vec3(LIGHT_TOP_DIR);
halfVector = normalize(lightDir + eye);
// Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
// Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
NdotL = max(dot(normal, lightDir), 0.0);
intensity_tainted = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
intensity_specular = 0.;
// if (NdotL > 0.0)
// intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), gl_FrontMaterial.shininess);
// Perform the same lighting calculation for the 2nd light source.
lightDir = vec3(LIGHT_FRONT_DIR);
halfVector = normalize(lightDir + eye);
NdotL = max(dot(normal, lightDir), 0.0);
intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE;
// compute the specular term if NdotL is larger than zero
if (NdotL > 0.0)
intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(normal, halfVector), 0.0), gl_FrontMaterial.shininess);
// Scaled to widths of the Z texture.
object_z = gl_Vertex.z / gl_Vertex.w;
gl_Position = ftransform();
}
VERTEX
}
sub _fragment_shader {
return <<'FRAGMENT';
#version 110
#define M_PI 3.1415926535897932384626433832795
// 2D texture (1D texture split by the rows) of color along the object Z axis.
uniform sampler2D z_texture;
// Scaling from the Z texture rows coordinate to the normalized texture row coordinate.
uniform float z_to_texture_row;
uniform float z_texture_row_to_normalized;
varying float intensity_specular;
varying float intensity_tainted;
varying float object_z;
uniform float z_cursor;
uniform float z_cursor_band_width;
void main()
{
float object_z_row = z_to_texture_row * object_z;
// Index of the row in the texture.
float z_texture_row = floor(object_z_row);
// Normalized coordinate from 0. to 1.
float z_texture_col = object_z_row - z_texture_row;
// float z_blend = 0.5 + 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) / 3.)));
// float z_blend = 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor)))) + 0.5;
float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25;
// Scale z_texture_row to normalized coordinates.
// Sample the Z texture.
gl_FragColor =
vec4(intensity_specular, intensity_specular, intensity_specular, 1.) +
// intensity_tainted * texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5)), -2.5);
(1. - z_blend) * intensity_tainted * texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5)), -200.) +
z_blend * vec4(1., 1., 0., 0.);
// and reset the transparency.
gl_FragColor.a = 1.;
}
FRAGMENT
}
# Container for object geometry and selection status.
package Slic3r::GUI::3DScene::Volume;
use Moo;
@ -1076,6 +1495,8 @@ use Moo;
has 'bounding_box' => (is => 'ro', required => 1);
has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) });
has 'color' => (is => 'ro', required => 1);
# An ID containing the object ID, volume ID and instance ID.
has 'composite_id' => (is => 'rw', default => sub { -1 });
# An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance.
has 'select_group_id' => (is => 'rw', default => sub { -1 });
# An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance.
@ -1097,6 +1518,26 @@ has 'tverts' => (is => 'rw');
# The offsets stores tripples of (z_top, qverts_idx, tverts_idx) in a linear array.
has 'offsets' => (is => 'rw');
# RGBA texture along the Z axis of an object, to visualize layers by stripes colored by their height.
has 'layer_height_texture_data' => (is => 'rw');
# Number of texture cells.
has 'layer_height_texture_cells' => (is => 'rw');
sub object_idx {
my ($self) = @_;
return $self->composite_id / 1000000;
}
sub volume_idx {
my ($self) = @_;
return ($self->composite_id / 1000) % 1000;
}
sub instance_idx {
my ($self) = @_;
return $self->composite_id % 1000;
}
sub transformed_bounding_box {
my ($self) = @_;
@ -1122,8 +1563,6 @@ __PACKAGE__->mk_accessors(qw(
color_by
select_by
drag_by
volumes_by_object
_objects_by_volumes
));
sub new {
@ -1133,14 +1572,12 @@ sub new {
$self->color_by('volume'); # object | volume
$self->select_by('object'); # object | volume | instance
$self->drag_by('instance'); # object | instance
$self->volumes_by_object({}); # obj_idx => [ volume_idx, volume_idx ... ]
$self->_objects_by_volumes({}); # volume_idx => [ obj_idx, instance_idx ]
return $self;
}
sub load_object {
my ($self, $model, $obj_idx, $instance_idxs) = @_;
my ($self, $model, $print, $obj_idx, $instance_idxs) = @_;
my $model_object;
if ($model->isa('Slic3r::Model::Object')) {
@ -1153,6 +1590,19 @@ sub load_object {
$instance_idxs ||= [0..$#{$model_object->instances}];
# Object will have a single common layer height texture for all volumes.
my $layer_height_texture_data;
my $layer_height_texture_cells;
if ($print && $obj_idx < $print->object_count) {
# Generate the layer height texture. Allocate data for the 0th and 1st mipmap levels.
$layer_height_texture_data = OpenGL::Array->new($self->{layer_preview_z_texture_width}*$self->{layer_preview_z_texture_height}*5, GL_UNSIGNED_BYTE);
# $print->get_object($obj_idx)->update_layer_height_profile_from_ranges();
$layer_height_texture_cells = $print->get_object($obj_idx)->generate_layer_height_texture(
$layer_height_texture_data->ptr,
$self->{layer_preview_z_texture_height},
$self->{layer_preview_z_texture_width});
}
my @volumes_idx = ();
foreach my $volume_idx (0..$#{$model_object->volumes}) {
my $volume = $model_object->volumes->[$volume_idx];
@ -1174,16 +1624,18 @@ sub load_object {
# not correspond to the color of the filament.
my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ];
$color->[3] = $volume->modifier ? 0.5 : 1;
print "Reloading object $volume_idx, $instance_idx\n";
push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new(
bounding_box => $mesh->bounding_box,
color => $color,
);
$v->composite_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx);
if ($self->select_by eq 'object') {
$v->select_group_id($obj_idx*1000000);
} elsif ($self->select_by eq 'volume') {
$v->select_group_id($obj_idx*1000000 + $volume_idx*1000);
} elsif ($self->select_by eq 'instance') {
$v->select_group_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx);
$v->select_group_id($v->composite_id);
}
if ($self->drag_by eq 'object') {
$v->drag_group_id($obj_idx*1000);
@ -1191,15 +1643,18 @@ sub load_object {
$v->drag_group_id($obj_idx*1000 + $instance_idx);
}
push @volumes_idx, my $scene_volume_idx = $#{$self->volumes};
$self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $volume_idx, $instance_idx ];
my $verts = Slic3r::GUI::_3DScene::GLVertexArray->new;
$verts->load_mesh($mesh);
$v->tverts($verts);
if (! $volume->modifier) {
$v->layer_height_texture_data($layer_height_texture_data);
$v->layer_height_texture_cells($layer_height_texture_cells);
}
}
}
$self->volumes_by_object->{$obj_idx} = [@volumes_idx];
return @volumes_idx;
}
@ -1538,19 +1993,4 @@ sub _extrusionentity_to_verts {
$tverts);
}
sub object_idx {
my ($self, $volume_idx) = @_;
return $self->_objects_by_volumes->{$volume_idx}[0];
}
sub volume_idx {
my ($self, $volume_idx) = @_;
return $self->_objects_by_volumes->{$volume_idx}[1];
}
sub instance_idx {
my ($self, $volume_idx) = @_;
return $self->_objects_by_volumes->{$volume_idx}[2];
}
1;

184
lib/Slic3r/GUI/GLShader.pm Normal file
View File

@ -0,0 +1,184 @@
############################################################
#
# Stripped down from the Perl OpenGL::Shader package by Vojtech Bubnik
# to only support the GLSL shaders. The original source was not maintained
# and did not install properly through the CPAN archive, and it was unnecessary
# complex.
#
# Original copyright:
#
# Copyright 2007 Graphcomp - ALL RIGHTS RESERVED
# Author: Bob "grafman" Free - grafman@graphcomp.com
#
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
############################################################
package Slic3r::GUI::GLShader;
use OpenGL(':all');
# Avoid cloning this class by the worker threads.
sub CLONE_SKIP { 1 }
# Shader constructor
sub new
{
# Check for required OpenGL extensions
return undef if (OpenGL::glpCheckExtension('GL_ARB_shader_objects'));
return undef if (OpenGL::glpCheckExtension('GL_ARB_fragment_shader'));
return undef if (OpenGL::glpCheckExtension('GL_ARB_vertex_shader'));
return undef if (OpenGL::glpCheckExtension('GL_ARB_shading_language_100'));
# my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION);
# my $glsl_version_ARB = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB );
# print "GLSL version: $glsl_version, ARB: $glsl_version_ARB\n";
my $this = shift;
my $class = ref($this) || $this;
my($type) = @_;
my $self = {type => uc($type)};
bless($self,$class);
# Get GL_SHADING_LANGUAGE_VERSION_ARB
my $shader_ver = glGetString(0x8B8C);
$shader_ver =~ m|([\d\.]+)|;
$self->{version} = $1 || '0';
print
return $self;
}
# Shader destructor
# Must be disabled first
sub DESTROY
{
my($self) = @_;
if ($self->{program})
{
glDetachObjectARB($self->{program},$self->{fragment_id}) if ($self->{fragment_id});
glDetachObjectARB($self->{program},$self->{vertex_id}) if ($self->{vertex_id});
glDeleteProgramsARB_p($self->{program});
}
glDeleteProgramsARB_p($self->{fragment_id}) if ($self->{fragment_id});
glDeleteProgramsARB_p($self->{vertex_id}) if ($self->{vertex_id});
}
# Load shader strings
sub Load
{
my($self,$fragment,$vertex) = @_;
# Load fragment code
if ($fragment)
{
$self->{fragment_id} = glCreateShaderObjectARB(GL_FRAGMENT_SHADER);
return undef if (!$self->{fragment_id});
glShaderSourceARB_p($self->{fragment_id}, $fragment);
#my $frag = glGetShaderSourceARB_p($self->{fragment_id});
#print STDERR "Loaded fragment:\n$frag\n";
glCompileShaderARB($self->{fragment_id});
my $stat = glGetInfoLogARB_p($self->{fragment_id});
return "Fragment shader: $stat" if ($stat);
}
# Load vertex code
if ($vertex)
{
$self->{vertex_id} = glCreateShaderObjectARB(GL_VERTEX_SHADER);
return undef if (!$self->{vertex_id});
glShaderSourceARB_p($self->{vertex_id}, $vertex);
#my $vert = glGetShaderSourceARB_p($self->{vertex_id});
#print STDERR "Loaded vertex:\n$vert\n";
glCompileShaderARB($self->{vertex_id});
$stat = glGetInfoLogARB_p($self->{vertex_id});
return "Vertex shader: $stat" if ($stat);
}
# Link shaders
my $sp = glCreateProgramObjectARB();
glAttachObjectARB($sp, $self->{fragment_id}) if ($fragment);
glAttachObjectARB($sp, $self->{vertex_id}) if ($vertex);
glLinkProgramARB($sp);
my $linked = glGetObjectParameterivARB_p($sp, GL_OBJECT_LINK_STATUS_ARB);
if (!$linked)
{
$stat = glGetInfoLogARB_p($sp);
#print STDERR "Load shader: $stat\n";
return "Link shader: $stat" if ($stat);
return 'Unable to link shader';
}
$self->{program} = $sp;
return '';
}
# Enable shader
sub Enable
{
my($self) = @_;
glUseProgramObjectARB($self->{program}) if ($self->{program});
}
# Disable shader
sub Disable
{
my($self) = @_;
glUseProgramObjectARB(0) if ($self->{program});
}
# Return shader vertex attribute ID
sub MapAttr
{
my($self,$attr) = @_;
return undef if (!$self->{program});
my $id = glGetAttribLocationARB_p($self->{program},$attr);
return undef if ($id < 0);
return $id;
}
# Return shader uniform variable ID
sub Map
{
my($self,$var) = @_;
return undef if (!$self->{program});
my $id = glGetUniformLocationARB_p($self->{program},$var);
return undef if ($id < 0);
return $id;
}
# Set shader vector
sub SetVector
{
my($self,$var,@values) = @_;
my $id = $self->Map($var);
return 'Unable to map $var' if (!defined($id));
my $count = scalar(@values);
eval('glUniform'.$count.'fARB($id,@values)');
return '';
}
# Set shader 4x4 matrix
sub SetMatrix
{
my($self,$var,$oga) = @_;
my $id = $self->Map($var);
return 'Unable to map $var' if (!defined($id));
glUniformMatrix4fvARB_c($id,1,0,$oga->ptr());
return '';
}
1;
__END__

View File

@ -42,6 +42,11 @@ sub new {
$self->_init_tabpanel;
$self->_init_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' ); });

View File

@ -8,10 +8,11 @@ use utf8;
use File::Basename qw(basename dirname);
use List::Util qw(sum first max);
use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad);
use LWP::UserAgent;
use threads::shared qw(shared_clone);
use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
:panel :sizer :toolbar :window wxTheApp :notebook :combobox);
use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
:panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap);
use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED);
use base 'Wx::Panel';
@ -30,6 +31,7 @@ use constant TB_SCALE => &Wx::NewId;
use constant TB_SPLIT => &Wx::NewId;
use constant TB_CUT => &Wx::NewId;
use constant TB_SETTINGS => &Wx::NewId;
use constant TB_LAYER_EDITING => &Wx::NewId;
# package variables to avoid passing lexicals to threads
our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType;
@ -94,7 +96,7 @@ sub new {
# Initialize 3D plater
if ($Slic3r::GUI::have_OpenGL) {
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config});
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config});
$self->{preview_notebook}->AddPage($self->{canvas3D}, '3D');
$self->{canvas3D}->set_on_select_object($on_select_object);
$self->{canvas3D}->set_on_double_click($on_double_click);
@ -154,6 +156,10 @@ sub new {
$self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), '');
$self->{htoolbar}->AddSeparator;
$self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), '');
# FIXME add a button for layer editing
$self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, undef, 'Layer Editing')
if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1);
} else {
my %tbar_buttons = (
add => "Add…",
@ -168,12 +174,17 @@ sub new {
split => "Split",
cut => "Cut…",
settings => "Settings…",
layer_editing => "Layer editing",
);
$self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) {
$self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
$self->{btoolbar}->Add($self->{"btn_$_"});
}
if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1) {
$self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
$self->{btoolbar}->Add($self->{"btn_layer_editing"});
}
}
$self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize,
@ -256,6 +267,11 @@ sub new {
EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; });
EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog });
EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog });
EVT_TOOL($self, TB_LAYER_EDITING, sub {
my $state = $self->{canvas3D}->layer_editing_enabled;
$self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state);
$self->on_layer_editing_toggled(! $state);
});
} else {
EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; });
EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove
@ -269,6 +285,7 @@ sub new {
EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; });
EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog });
EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog });
EVT_TOGGLEBUTTON($self, $self->{btn_layer_editing}, sub { $self->on_layer_editing_toggled($self->{btn_layer_editing}->GetValue); });
}
$_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
@ -464,6 +481,12 @@ sub _on_select_preset {
$self->on_config_change($self->GetFrame->config);
}
sub on_layer_editing_toggled {
my ($self, $new_state) = @_;
$self->{canvas3D}->layer_editing_enabled($new_state);
$self->{canvas3D}->update;
}
sub GetFrame {
my ($self) = @_;
return &Wx::GetTopLevelParent($self);
@ -1482,6 +1505,8 @@ sub on_thumbnail_made {
sub update {
my ($self, $force_autocenter) = @_;
print "Platter - update\n";
if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) {
$self->{model}->center_instances_around_point($self->bed_centerf);
}
@ -1679,7 +1704,7 @@ sub object_list_changed {
my $have_objects = @{$self->{objects}} ? 1 : 0;
my $method = $have_objects ? 'Enable' : 'Disable';
$self->{"btn_$_"}->$method
for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode);
for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode layer_editing);
if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) {
$self->{btn_reslice}->Disable;
@ -1712,6 +1737,7 @@ sub selection_changed {
if ($self->{object_info_size}) { # have we already loaded the info pane?
if ($have_sel) {
my $model_object = $self->{model}->objects->[$obj_idx];
$model_object->print_info;
my $model_instance = $model_object->instances->[0];
$self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size}));
$self->{object_info_materials}->SetLabel($model_object->materials_count);

View File

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

View File

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

View File

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

View File

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

View File

@ -523,6 +523,7 @@ sub build {
max_volumetric_extrusion_rate_slope_positive max_volumetric_extrusion_rate_slope_negative
perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed
solid_infill_speed top_solid_infill_speed support_material_speed
support_material_xy_spacing
support_material_interface_speed bridge_speed gap_fill_speed
travel_speed
first_layer_speed
@ -532,8 +533,8 @@ sub build {
brim_width
support_material support_material_threshold support_material_enforce_layers
raft_layers
support_material_pattern support_material_with_sheath support_material_spacing support_material_angle
support_material_interface_layers support_material_interface_spacing
support_material_pattern support_material_with_sheath support_material_spacing support_material_synchronize_layers support_material_angle
support_material_interface_layers support_material_interface_spacing support_material_interface_contact_loops
support_material_contact_distance support_material_buildplate_only dont_support_bridges
notes
complete_objects extruder_clearance_radius extruder_clearance_height
@ -646,8 +647,11 @@ sub build {
$optgroup->append_single_option_line('support_material_angle');
$optgroup->append_single_option_line('support_material_interface_layers');
$optgroup->append_single_option_line('support_material_interface_spacing');
$optgroup->append_single_option_line('support_material_interface_contact_loops');
$optgroup->append_single_option_line('support_material_buildplate_only');
$optgroup->append_single_option_line('support_material_xy_spacing');
$optgroup->append_single_option_line('dont_support_bridges');
$optgroup->append_single_option_line('support_material_synchronize_layers');
}
}
@ -910,12 +914,12 @@ sub _update {
my $have_support_interface = $config->support_material_interface_layers > 0;
$self->get_field($_)->toggle($have_support_material)
for qw(support_material_threshold support_material_pattern support_material_with_sheath
support_material_spacing support_material_angle
support_material_spacing support_material_synchronize_layers support_material_angle
support_material_interface_layers dont_support_bridges
support_material_extrusion_width support_material_contact_distance);
support_material_extrusion_width support_material_contact_distance support_material_xy_spacing);
$self->get_field($_)->toggle($have_support_material && $have_support_interface)
for qw(support_material_interface_spacing support_material_interface_extruder
support_material_interface_speed);
support_material_interface_speed support_material_interface_contact_loops);
$self->get_field('perimeter_extrusion_width')->toggle($have_perimeters || $have_skirt || $have_brim);
$self->get_field('support_material_extruder')->toggle($have_support_material || $have_skirt);

View File

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

View File

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

View File

@ -262,7 +262,10 @@ sub export {
$gcodegen->avoid_crossing_perimeters->set_disable_once(1);
}
my @layers = sort { $a->print_z <=> $b->print_z } @{$object->layers}, @{$object->support_layers};
# Order layers by print_z, support layers preceding the object layers.
my @layers = sort
{ ($a->print_z == $b->print_z) ? ($a->isa('Slic3r::Layer::Support') ? -1 : 0) : $a->print_z <=> $b->print_z }
@{$object->layers}, @{$object->support_layers};
for my $layer (@layers) {
# if we are printing the bottom layer of an object, and we have already finished
# another one, set first layer temperatures. this happens before the Z move
@ -289,7 +292,8 @@ sub export {
my %layers = (); # print_z => [ [layers], [layers], [layers] ] by obj_idx
foreach my $obj_idx (0 .. ($self->print->object_count - 1)) {
my $object = $self->objects->[$obj_idx];
foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
# Collect the object layers by z, support layers first, object layers second.
foreach my $layer (@{$object->support_layers}, @{$object->layers}) {
$layers{ $layer->print_z } ||= [];
$layers{ $layer->print_z }[$obj_idx] ||= [];
push @{$layers{ $layer->print_z }[$obj_idx]}, $layer;

View File

@ -7,7 +7,7 @@ use List::Util qw(min max sum first);
use Slic3r::Flow ':roles';
use Slic3r::Geometry qw(X Y Z PI scale unscale chained_path epsilon);
use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex
offset offset_ex offset2 offset2_ex intersection_ppl CLIPPER_OFFSET_SCALE JT_MITER);
offset offset_ex offset2 offset2_ex JT_MITER);
use Slic3r::Print::State ':steps';
use Slic3r::Surface ':types';
@ -45,223 +45,7 @@ sub slice {
$self->set_step_started(STEP_SLICE);
$self->print->status_cb->(10, "Processing triangulated mesh");
# init layers
{
$self->clear_layers;
# make layers taking custom heights into account
my $id = 0;
my $print_z = 0;
my $first_object_layer_height = -1;
my $first_object_layer_distance = -1;
# add raft layers
if ($self->config->raft_layers > 0) {
# Reserve object layers for the raft. Last layer of the raft is the contact layer.
$id += $self->config->raft_layers;
# Raise first object layer Z by the thickness of the raft itself
# plus the extra distance required by the support material logic.
#FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case.
my $first_layer_height = $self->config->get_value('first_layer_height');
$print_z += $first_layer_height;
# Use as large as possible layer height for the intermediate raft layers.
my $support_material_layer_height;
{
my @nozzle_diameters = (
map $self->print->config->get_at('nozzle_diameter', $_),
$self->config->support_material_extruder-1,
$self->config->support_material_interface_extruder-1,
);
$support_material_layer_height = 0.75 * min(@nozzle_diameters);
}
$print_z += $support_material_layer_height * ($self->config->raft_layers - 1);
# compute the average of all nozzles used for printing the object
#FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa?
my $nozzle_diameter;
{
my @nozzle_diameters = (
map $self->print->config->get_at('nozzle_diameter', $_), @{$self->print->object_extruders}
);
$nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;
}
$first_object_layer_distance = $self->_support_material->contact_distance($self->config->layer_height, $nozzle_diameter);
# force first layer print_z according to the contact distance
# (the loop below will raise print_z by such height)
$first_object_layer_height = $first_object_layer_distance - $self->config->support_material_contact_distance;
}
# loop until we have at least one layer and the max slice_z reaches the object height
my $slice_z = 0;
my $height = 0;
my $max_z = unscale($self->size->z);
while (($slice_z - $height) <= $max_z) {
# assign the default height to the layer according to the general settings
$height = ($id == 0)
? $self->config->get_value('first_layer_height')
: $self->config->layer_height;
# look for an applicable custom range
if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) {
$height = $range->[2];
# if user set custom height to zero we should just skip the range and resume slicing over it
if ($height == 0) {
$slice_z += $range->[1] - $range->[0];
next;
}
}
if ($first_object_layer_height != -1 && !@{$self->layers}) {
$height = $first_object_layer_height;
$print_z += ($first_object_layer_distance - $height);
}
$print_z += $height;
$slice_z += $height/2;
### Slic3r::debugf "Layer %d: height = %s; slice_z = %s; print_z = %s\n", $id, $height, $slice_z, $print_z;
$self->add_layer($id, $height, $print_z, $slice_z);
if ($self->layer_count >= 2) {
my $lc = $self->layer_count;
$self->get_layer($lc - 2)->set_upper_layer($self->get_layer($lc - 1));
$self->get_layer($lc - 1)->set_lower_layer($self->get_layer($lc - 2));
}
$id++;
$slice_z += $height/2; # add the other half layer
}
}
# make sure all layers contain layer region objects for all regions
my $regions_count = $self->print->region_count;
foreach my $layer (@{ $self->layers }) {
$layer->region($_) for 0 .. ($regions_count-1);
}
# get array of Z coordinates for slicing
my @z = map $_->slice_z, @{$self->layers};
# slice all non-modifier volumes
for my $region_id (0..($self->region_count - 1)) {
my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 0);
for my $layer_id (0..$#$expolygons_by_layer) {
my $layerm = $self->get_layer($layer_id)->regions->[$region_id];
$layerm->slices->clear;
foreach my $expolygon (@{ $expolygons_by_layer->[$layer_id] }) {
$layerm->slices->append(Slic3r::Surface->new(
expolygon => $expolygon,
surface_type => S_TYPE_INTERNAL,
));
}
}
}
# then slice all modifier volumes
if ($self->region_count > 1) {
for my $region_id (0..$self->region_count) {
my $expolygons_by_layer = $self->_slice_region($region_id, \@z, 1);
# loop through the other regions and 'steal' the slices belonging to this one
for my $other_region_id (0..$self->region_count) {
next if $other_region_id == $region_id;
for my $layer_id (0..$#$expolygons_by_layer) {
my $layerm = $self->get_layer($layer_id)->regions->[$region_id];
my $other_layerm = $self->get_layer($layer_id)->regions->[$other_region_id];
next if !defined $other_layerm;
my $other_slices = [ map $_->p, @{$other_layerm->slices} ]; # Polygons
my $my_parts = intersection_ex(
$other_slices,
[ map @$_, @{ $expolygons_by_layer->[$layer_id] } ],
);
next if !@$my_parts;
# append new parts to our region
foreach my $expolygon (@$my_parts) {
$layerm->slices->append(Slic3r::Surface->new(
expolygon => $expolygon,
surface_type => S_TYPE_INTERNAL,
));
}
# remove such parts from original region
$other_layerm->slices->clear;
$other_layerm->slices->append(Slic3r::Surface->new(
expolygon => $_,
surface_type => S_TYPE_INTERNAL,
)) for @{ diff_ex($other_slices, [ map @$_, @$my_parts ]) };
}
}
}
}
# remove last layer(s) if empty
$self->delete_layer($self->layer_count - 1)
while $self->layer_count && (!map @{$_->slices}, @{$self->get_layer($self->layer_count - 1)->regions});
foreach my $layer (@{ $self->layers }) {
# apply size compensation
if ($self->config->xy_size_compensation != 0) {
my $delta = scale($self->config->xy_size_compensation);
if (@{$layer->regions} == 1) {
# single region
my $layerm = $layer->regions->[0];
my $slices = [ map $_->p, @{$layerm->slices} ];
$layerm->slices->clear;
$layerm->slices->append(Slic3r::Surface->new(
expolygon => $_,
surface_type => S_TYPE_INTERNAL,
)) for @{offset_ex($slices, $delta)};
} else {
if ($delta < 0) {
# multiple regions, shrinking
# we apply the offset to the combined shape, then intersect it
# with the original slices for each region
my $slices = union([ map $_->p, map @{$_->slices}, @{$layer->regions} ]);
$slices = offset($slices, $delta);
foreach my $layerm (@{$layer->regions}) {
my $this_slices = intersection_ex(
$slices,
[ map $_->p, @{$layerm->slices} ],
);
$layerm->slices->clear;
$layerm->slices->append(Slic3r::Surface->new(
expolygon => $_,
surface_type => S_TYPE_INTERNAL,
)) for @$this_slices;
}
} else {
# multiple regions, growing
# this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap
# so we give priority to the first one and so on
for my $i (0..$#{$layer->regions}) {
my $layerm = $layer->regions->[$i];
my $slices = offset_ex([ map $_->p, @{$layerm->slices} ], $delta);
if ($i > 0) {
$slices = diff_ex(
[ map @$_, @$slices ],
[ map $_->p, map @{$_->slices}, map $layer->regions->[$_], 0..($i-1) ], # slices of already processed regions
);
}
$layerm->slices->clear;
$layerm->slices->append(Slic3r::Surface->new(
expolygon => $_,
surface_type => S_TYPE_INTERNAL,
)) for @$slices;
}
}
}
}
# Merge all regions' slices to get islands, chain them by a shortest path.
$layer->make_slices;
}
$self->_slice;
# detect slicing errors
my $warning_thrown = 0;
@ -334,151 +118,15 @@ sub slice {
$self->set_step_done(STEP_SLICE);
}
# called from slice()
sub _slice_region {
my ($self, $region_id, $z, $modifier) = @_;
return [] if !@{$self->get_region_volumes($region_id)};
# compose mesh
my $mesh;
foreach my $volume_id (@{ $self->get_region_volumes($region_id) }) {
my $volume = $self->model_object->volumes->[$volume_id];
next if $volume->modifier && !$modifier;
next if !$volume->modifier && $modifier;
if (defined $mesh) {
$mesh->merge($volume->mesh);
} else {
$mesh = $volume->mesh->clone;
}
}
return if !defined $mesh;
# transform mesh
# we ignore the per-instance transformations currently and only
# consider the first one
$self->model_object->instances->[0]->transform_mesh($mesh, 1);
# align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
$mesh->translate((map unscale(-$_), @{$self->_copies_shift}), -$self->model_object->bounding_box->z_min);
# perform actual slicing
return $mesh->slice($z);
}
# 1) Merges typed region slices into stInternal type.
# 2) Increases an "extra perimeters" counter at region slices where needed.
# 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
sub make_perimeters {
my $self = shift;
my ($self) = @_;
# prerequisites
$self->slice;
return if $self->step_done(STEP_PERIMETERS);
$self->set_step_started(STEP_PERIMETERS);
$self->print->status_cb->(20, "Generating perimeters");
# Merge region slices if they were split into types.
# FIXME this is using a safety offset, so the region slices will be slightly bigger with each iteration.
if ($self->typed_slices) {
$_->merge_slices for @{$self->layers};
$self->set_typed_slices(0);
$self->invalidate_step(STEP_PREPARE_INFILL);
}
# compare each layer to the one below, and mark those slices needing
# one additional inner perimeter, like the top of domed objects-
# this algorithm makes sure that at least one perimeter is overlapping
# but we don't generate any extra perimeter if fill density is zero, as they would be floating
# inside the object - infill_only_where_needed should be the method of choice for printing
# hollow objects
for my $region_id (0 .. ($self->print->region_count-1)) {
my $region = $self->print->regions->[$region_id];
my $region_perimeters = $region->config->perimeters;
next if !$region->config->extra_perimeters;
next if $region_perimeters == 0;
next if $region->config->fill_density == 0;
for my $i (0 .. ($self->layer_count - 2)) {
my $layerm = $self->get_layer($i)->get_region($region_id);
my $upper_layerm = $self->get_layer($i+1)->get_region($region_id);
my $upper_layerm_polygons = [ map $_->p, @{$upper_layerm->slices} ];
# Filter upper layer polygons in intersection_ppl by their bounding boxes?
# my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
my $total_loop_length = sum(map $_->length, @$upper_layerm_polygons) // 0;
my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
my $ext_perimeter_flow = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER);
my $ext_perimeter_width = $ext_perimeter_flow->scaled_width;
my $ext_perimeter_spacing = $ext_perimeter_flow->scaled_spacing;
foreach my $slice (@{$layerm->slices}) {
while (1) {
# compute the total thickness of perimeters
my $perimeters_thickness = $ext_perimeter_width/2 + $ext_perimeter_spacing/2
+ ($region_perimeters-1 + $slice->extra_perimeters) * $perimeter_spacing;
# define a critical area where we don't want the upper slice to fall into
# (it should either lay over our perimeters or outside this area)
my $critical_area_depth = $perimeter_spacing*1.5;
my $critical_area = diff(
offset($slice->expolygon->arrayref, -$perimeters_thickness),
offset($slice->expolygon->arrayref, -($perimeters_thickness + $critical_area_depth)),
);
# check whether a portion of the upper slices falls inside the critical area
my $intersection = intersection_ppl(
$upper_layerm_polygons,
$critical_area,
);
# only add an additional loop if at least 30% of the slice loop would benefit from it
my $total_intersection_length = sum(map $_->length, @$intersection) // 0;
last unless $total_intersection_length > $total_loop_length*0.3;
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"extra.svg",
no_arrows => 1,
expolygons => union_ex($critical_area),
polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
);
}
$slice->extra_perimeters($slice->extra_perimeters + 1);
}
Slic3r::debugf " adding %d more perimeter(s) at layer %d\n",
$slice->extra_perimeters, $layerm->layer->id
if $slice->extra_perimeters > 0;
}
}
}
Slic3r::parallelize(
threads => $self->print->config->threads,
items => sub { 0 .. ($self->layer_count - 1) },
thread_cb => sub {
my $q = shift;
while (defined (my $i = $q->dequeue)) {
$self->get_layer($i)->make_perimeters;
}
},
no_threads_cb => sub {
$_->make_perimeters for @{$self->layers};
},
);
# simplify slices (both layer and region slices),
# we only need the max resolution for perimeters
### This makes this method not-idempotent, so we keep it disabled for now.
###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
$self->set_step_done(STEP_PERIMETERS);
$self->_make_perimeters;
}
sub prepare_infill {
@ -598,32 +246,7 @@ sub infill {
# prerequisites
$self->prepare_infill;
return if $self->step_done(STEP_INFILL);
$self->set_step_started(STEP_INFILL);
$self->print->status_cb->(70, "Infilling layers");
Slic3r::parallelize(
threads => $self->print->config->threads,
items => sub { 0..$#{$self->layers} },
thread_cb => sub {
my $q = shift;
while (defined (my $i = $q->dequeue)) {
$self->get_layer($i)->make_fills;
}
},
no_threads_cb => sub {
foreach my $layer (@{$self->layers}) {
$layer->make_fills;
}
},
);
### we could free memory now, but this would make this step not idempotent
### Vojtech: Cannot release the fill_surfaces, they are used by the support generator.
### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
$self->set_step_done(STEP_INFILL);
$self->_infill;
}
sub generate_support_material {
@ -637,44 +260,35 @@ sub generate_support_material {
$self->clear_support_layers;
if ((!$self->config->support_material && $self->config->raft_layers == 0) || scalar(@{$self->layers}) < 2) {
$self->set_step_done(STEP_SUPPORTMATERIAL);
return;
if (($self->config->support_material || $self->config->raft_layers > 0) && scalar(@{$self->layers}) > 1) {
$self->print->status_cb->(85, "Generating support material");
if (0) {
# Old supports, Perl implementation.
my $first_layer_flow = Slic3r::Flow->new_from_width(
width => ($self->print->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
role => FLOW_ROLE_SUPPORT_MATERIAL,
nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ]
// $self->print->config->nozzle_diameter->[0],
layer_height => $self->config->get_abs_value('first_layer_height'),
bridge_flow_ratio => 0,
);
my $support_material = Slic3r::Print::SupportMaterial->new(
print_config => $self->print->config,
object_config => $self->config,
first_layer_flow => $first_layer_flow,
flow => $self->support_material_flow,
interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE),
);
$support_material->generate($self);
} else {
# New supports, C++ implementation.
$self->_generate_support_material;
}
}
$self->print->status_cb->(85, "Generating support material");
$self->_support_material->generate($self);
$self->set_step_done(STEP_SUPPORTMATERIAL);
}
sub _support_material {
my ($self) = @_;
my $first_layer_flow = Slic3r::Flow->new_from_width(
width => ($self->print->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
role => FLOW_ROLE_SUPPORT_MATERIAL,
nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ]
// $self->print->config->nozzle_diameter->[0],
layer_height => $self->config->get_abs_value('first_layer_height'),
bridge_flow_ratio => 0,
);
if (1) {
# Old supports, Perl implementation.
return Slic3r::Print::SupportMaterial->new(
print_config => $self->print->config,
object_config => $self->config,
first_layer_flow => $first_layer_flow,
flow => $self->support_material_flow,
interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE),
);
} else {
# New supports, C++ implementation.
return Slic3r::Print::SupportMaterial2->new($self);
}
}
# Idempotence of this method is guaranteed by the fact that we don't remove things from
# fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries.
sub clip_fill_surfaces {
@ -863,7 +477,7 @@ sub discover_horizontal_shells {
# and it's not wanted in a hollow print even if it would make sense when
# obeying the solid shell count option strictly (DWIM!)
my $margin = $neighbor_layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width;
my $regularized = offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5);
my $regularized = offset2($new_internal_solid, -$margin, +$margin, JT_MITER, 5);
my $too_narrow = diff(
$new_internal_solid,
$regularized,
@ -893,7 +507,7 @@ sub discover_horizontal_shells {
# have the same angle, so the next shell would be grown even more and so on.
my $too_narrow = diff(
$new_internal_solid,
offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5),
offset2($new_internal_solid, -$margin, +$margin, JT_MITER, 5),
1,
);

View File

@ -8,7 +8,7 @@ use Slic3r::ExtrusionPath ':roles';
use Slic3r::Flow ':roles';
use Slic3r::Geometry qw(epsilon scale scaled_epsilon PI rad2deg deg2rad convex_hull);
use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2
intersection_pl offset2_ex diff_pl CLIPPER_OFFSET_SCALE JT_MITER JT_ROUND);
intersection_pl offset2_ex diff_pl JT_MITER JT_ROUND);
use Slic3r::Surface ':types';
has 'print_config' => (is => 'rw', required => 1);
@ -20,6 +20,7 @@ has 'interface_flow' => (is => 'rw', required => 1);
use constant DEBUG_CONTACT_ONLY => 0;
# increment used to reach MARGIN in steps to avoid trespassing thin objects
use constant MARGIN => 1.5;
use constant MARGIN_STEP => MARGIN/3;
# generate a tree-like structure to save material
@ -45,6 +46,7 @@ sub generate {
# We now know the upper and lower boundaries for our support material object
# (@$contact_z and @$top_z), so we can generate intermediate layers.
my $support_z = $self->support_layers_z(
$object,
[ sort keys %$contact ],
[ sort keys %$top ],
max(map $_->height, @{$object->layers})
@ -299,9 +301,8 @@ sub contact_area {
offset(
$diff,
$_,
CLIPPER_OFFSET_SCALE,
JT_ROUND,
scale(0.05)*CLIPPER_OFFSET_SCALE),
scale(0.05)),
$slices_margin
);
}
@ -371,7 +372,7 @@ sub object_top {
# grow top surfaces so that interface and support generation are generated
# with some spacing from object - it looks we don't need the actual
# top shapes so this can be done here
$top{ $layer->print_z } = offset($touching, $self->flow->scaled_width);
$top{ $layer->print_z } = offset($touching, $self->flow->scaled_width + $self->object_config->support_material_xy_spacing);
}
# remove the areas that touched from the projection that will continue on
@ -384,7 +385,7 @@ sub object_top {
}
sub support_layers_z {
my ($self, $contact_z, $top_z, $max_object_layer_height) = @_;
my ($self, $object, $contact_z, $top_z, $max_object_layer_height) = @_;
# quick table to check whether a given Z is a top surface
my %top = map { $_ => 1 } @$top_z;
@ -397,13 +398,18 @@ sub support_layers_z {
my $contact_distance = $self->contact_distance($support_material_height, $nozzle_diameter);
# initialize known, fixed, support layers
my @z = sort { $a <=> $b }
@$contact_z,
# TODO: why we have this?
# Vojtech: To detect the bottom interface layers by finding a Z value in the $top_z.
@$top_z,
# Top surfaces of the bottom interface layers.
(map $_ + $contact_distance, @$top_z);
my @z = @$contact_z;
my $synchronize = $self->object_config->support_material_synchronize_layers;
if (! $synchronize) {
push @z,
# TODO: why we have this?
# Vojtech: To detect the bottom interface layers by finding a Z value in the $top_z.
@$top_z;
push @z,
# Top surfaces of the bottom interface layers.
(map $_ + $contact_distance, @$top_z);
}
@z = sort { $a <=> $b } @z;
# enforce first layer height
my $first_layer_height = $self->object_config->get_value('first_layer_height');
@ -423,23 +429,29 @@ sub support_layers_z {
1..($self->object_config->raft_layers - 2);
}
# create other layers (skip raft layers as they're already done and use thicker layers)
for (my $i = $#z; $i >= $self->object_config->raft_layers; $i--) {
my $target_height = $support_material_height;
if ($i > 0 && $top{ $z[$i-1] }) {
# Bridge flow?
#FIXME We want to enforce not only the bridge flow height, but also the interface gap!
# This will introduce an additional layer if the gap is set to an extreme value!
$target_height = $nozzle_diameter;
}
if ($synchronize) {
@z = splice @z, $self->object_config->raft_layers;
# if ($self->object_config->raft_layers > scalar(@z));
push @z, map $_->print_z, @{$object->layers};
} else {
# create other layers (skip raft layers as they're already done and use thicker layers)
for (my $i = $#z; $i >= $self->object_config->raft_layers; $i--) {
my $target_height = $support_material_height;
if ($i > 0 && $top{ $z[$i-1] }) {
# Bridge flow?
#FIXME We want to enforce not only the bridge flow height, but also the interface gap!
# This will introduce an additional layer if the gap is set to an extreme value!
$target_height = $nozzle_diameter;
}
# enforce first layer height
#FIXME better to split the layers regularly, than to bite a constant height one at a time,
# and then be left with a very thin layer at the end.
if (($i == 0 && $z[$i] > $target_height + $first_layer_height)
|| ($z[$i] - $z[$i-1] > $target_height + Slic3r::Geometry::epsilon)) {
splice @z, $i, 0, ($z[$i] - $target_height);
$i++;
# enforce first layer height
#FIXME better to split the layers regularly, than to bite a constant height one at a time,
# and then be left with a very thin layer at the end.
if (($i == 0 && $z[$i] > $target_height + $first_layer_height)
|| ($z[$i] - $z[$i-1] > $target_height + Slic3r::Geometry::epsilon)) {
splice @z, $i, 0, ($z[$i] - $target_height);
$i++;
}
}
}
@ -584,9 +596,8 @@ sub generate_base_layers {
$fillet_radius_scaled,
-$fillet_radius_scaled,
# Use a geometric offsetting for filleting.
CLIPPER_OFFSET_SCALE,
JT_ROUND,
0.2*$fillet_radius_scaled*CLIPPER_OFFSET_SCALE),
0.2*$fillet_radius_scaled),
$trim_polygons,
0); # don't apply the safety offset.
}
@ -613,10 +624,11 @@ sub clip_with_object {
# $layer->slices contains the full shape of layer, thus including
# perimeter's width. $support contains the full shape of support
# material, thus including the width of its foremost extrusion.
# We leave a gap equal to a full extrusion width.
# We leave a gap equal to a full extrusion width + an offset
# if the user wants to play around with this setting.
$support->{$i} = diff(
$support->{$i},
offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow->scaled_width),
offset([ map @$_, map @{$_->slices}, @layers ], +$self->object_config->support_material_xy_spacing),
);
}
}
@ -628,8 +640,8 @@ sub generate_toolpaths {
my $interface_flow = $self->interface_flow;
# shape of contact area
my $contact_loops = 1;
my $circle_radius = 1.5 * $interface_flow->scaled_width;
my $contact_loops = $self->object_config->support_material_interface_contact_loops ? 1 : 0;
my $circle_radius = 1.5 * $interface_flow->scaled_width - ($self->object_config->support_material_xy_spacing / 0.000001) ;
my $circle_distance = 3 * $circle_radius;
my $circle = Slic3r::Polygon->new(map [ $circle_radius * cos $_, $circle_radius * sin $_ ],
(5*PI/3, 4*PI/3, PI, 2*PI/3, PI/3, 0));
@ -692,11 +704,14 @@ sub generate_toolpaths {
# if no interface layers were requested we treat the contact layer
# exactly as a generic base layer
push @$base, @$contact;
} elsif (@$contact && $contact_loops > 0) {
} elsif ($contact_loops == 0) {
# No contact loops, but some interface layers. Print the contact layer as a normal interface layer.
push @$interface, @$contact;
} elsif (@$contact) {
# generate the outermost loop
# find centerline of the external loop (or any other kind of extrusions should the loop be skipped)
$contact = offset($contact, -$_interface_flow->scaled_width/2);
$contact = offset($contact, -($_interface_flow->scaled_width + ($self->object_config->support_material_xy_spacing / 0.000001)) /2);
my @loops0 = ();
{

View File

@ -426,6 +426,9 @@ $j
Support material angle in degrees (range: 0-90, default: $config->{support_material_angle})
--support-material-contact-distance
Vertical distance between object and support material (0+, default: $config->{support_material_contact_distance})
--support-material-xy-spacing
"XY separation between an object and its support. If expressed as percentage (for example 50%),
it will be calculated over external perimeter width (default: half of exteral perimeter width)
--support-material-interface-layers
Number of perpendicular layers between support material and object (0+, default: $config->{support_material_interface_layers})
--support-material-interface-spacing

View File

@ -28,7 +28,7 @@ use Slic3r::Test;
interface_flow => $flow,
first_layer_flow => $flow,
);
my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height);
my $support_z = $support->support_layers_z($print->print->objects->[0], \@contact_z, \@top_z, $config->layer_height);
my $expected_top_spacing = $support->contact_distance($config->layer_height, $config->nozzle_diameter->[0]);
is $support_z->[0], $config->first_layer_height,

View File

@ -117,17 +117,21 @@ if (defined $ENV{BOOST_LIBRARYDIR}) {
# In order to generate the -l switches we need to know how Boost libraries are named
my $have_boost = 0;
my @boost_libraries = qw(system thread); # we need these
my @boost_libraries = qw(system thread log); # we need these
# check without explicit lib path (works on Linux)
if (! $mswin) {
$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,8 +142,7 @@ if (!$ENV{SLIC3R_STATIC} && $have_boost) {
# Try to find the boost system library.
my @files = glob "$path/${lib_prefix}system*$lib_ext";
next if !@files;
if ($files[0] =~ /${lib_prefix}system([^.]+)$lib_ext$/) {
if ($files[0] =~ /\Q${lib_prefix}system\E([^.]*)\Q$lib_ext\E$/) {
# Suffix contains the version number, the build type etc.
my $suffix = $1;
# Verify existence of all required boost libraries at $path.
@ -212,6 +215,10 @@ if ($cpp_guess->is_gcc) {
}
}
print "\n";
print 'With @INC: ', join(', ', map "\"$_\"", @INC), "\n";
print 'With @LIBS: ', join(', ', map "\"$_\"", @LIBS), "\n";
my $build = Module::Build::WithXSpp->new(
module_name => 'Slic3r::XS',
dist_abstract => 'XS code for Slic3r',

View File

@ -32,6 +32,8 @@ src/libslic3r/ExtrusionEntityCollection.cpp
src/libslic3r/ExtrusionEntityCollection.hpp
src/libslic3r/ExtrusionSimulator.cpp
src/libslic3r/ExtrusionSimulator.hpp
src/libslic3r/Fill/Fill.cpp
src/libslic3r/Fill/Fill.hpp
src/libslic3r/Fill/FillBase.cpp
src/libslic3r/Fill/FillBase.hpp
src/libslic3r/Fill/FillConcentric.cpp
@ -54,6 +56,8 @@ src/libslic3r/GCodeSender.cpp
src/libslic3r/GCodeSender.hpp
src/libslic3r/GCodeWriter.cpp
src/libslic3r/GCodeWriter.hpp
src/libslic3r/GCode/Analyzer.cpp
src/libslic3r/GCode/Analyzer.hpp
src/libslic3r/GCode/PressureEqualizer.cpp
src/libslic3r/GCode/PressureEqualizer.hpp
src/libslic3r/Geometry.cpp
@ -88,6 +92,10 @@ src/libslic3r/PrintConfig.cpp
src/libslic3r/PrintConfig.hpp
src/libslic3r/PrintObject.cpp
src/libslic3r/PrintRegion.cpp
src/libslic3r/Slicing.cpp
src/libslic3r/Slicing.hpp
src/libslic3r/SlicingAdaptive.cpp
src/libslic3r/SlicingAdaptive.hpp
src/libslic3r/SupportMaterial.cpp
src/libslic3r/SupportMaterial.hpp
src/libslic3r/Surface.cpp
@ -99,6 +107,7 @@ src/libslic3r/SVG.hpp
src/libslic3r/TriangleMesh.cpp
src/libslic3r/TriangleMesh.hpp
src/libslic3r/utils.cpp
src/libslic3r/Utils.hpp
src/perlglue.cpp
src/poly2tri/common/shapes.cc
src/poly2tri/common/shapes.h
@ -200,7 +209,6 @@ xsp/Polygon.xsp
xsp/Polyline.xsp
xsp/PolylineCollection.xsp
xsp/Print.xsp
xsp/SupportMaterial.xsp
xsp/Surface.xsp
xsp/SurfaceCollection.xsp
xsp/TriangleMesh.xsp

View File

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

View File

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

View File

@ -154,16 +154,34 @@ private:
void AddChild(PolyNode& child);
friend class Clipper; //to access Index
friend class ClipperOffset;
friend class PolyTree; //to implement the PolyTree::move operator
};
class PolyTree: public PolyNode
{
public:
~PolyTree(){Clear();};
PolyTree() {}
PolyTree(PolyTree &&src) { *this = std::move(src); }
virtual ~PolyTree(){Clear();};
PolyTree& operator=(PolyTree &&src) {
AllNodes = std::move(src.AllNodes);
Contour = std::move(src.Contour);
Childs = std::move(src.Childs);
Parent = nullptr;
Index = src.Index;
m_IsOpen = src.m_IsOpen;
m_jointype = src.m_jointype;
m_endtype = src.m_endtype;
for (size_t i = 0; i < Childs.size(); ++ i)
Childs[i]->Parent = this;
return *this;
}
PolyNode* GetFirst() const;
void Clear();
int Total() const;
private:
PolyTree(const PolyTree &src) = delete;
PolyTree& operator=(const PolyTree &src) = delete;
PolyNodes AllNodes;
friend class Clipper; //to access AllNodes
};

View File

@ -277,4 +277,26 @@ BoundingBoxBase<PointClass>::overlap(const BoundingBoxBase<PointClass> &other) c
template bool BoundingBoxBase<Point>::overlap(const BoundingBoxBase<Point> &point) const;
template bool BoundingBoxBase<Pointf>::overlap(const BoundingBoxBase<Pointf> &point) const;
// Align a coordinate to a grid. The coordinate may be negative,
// the aligned value will never be bigger than the original one.
static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
// Current C++ standard defines the result of integer division to be rounded to zero,
// for both positive and negative numbers. Here we want to round down for negative
// numbers as well.
coord_t aligned = (coord < 0) ?
((coord - spacing + 1) / spacing) * spacing :
(coord / spacing) * spacing;
assert(aligned <= coord);
return aligned;
}
void BoundingBox::align_to_grid(const coord_t cell_size)
{
if (this->defined) {
min.x = _align_to_grid(min.x, cell_size);
min.y = _align_to_grid(min.y, cell_size);
}
}
}

View File

@ -65,6 +65,9 @@ class BoundingBox : public BoundingBoxBase<Point>
BoundingBox rotated(double angle, const Point &center) const;
void rotate(double angle) { (*this) = this->rotated(angle); }
void rotate(double angle, const Point &center) { (*this) = this->rotated(angle, center); }
// Align the min corner to a grid of cell_size x cell_size cells,
// to encompass the original bounding box.
void align_to_grid(const coord_t cell_size);
BoundingBox() : BoundingBoxBase<Point>() {};
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {};

View File

@ -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;
// Get anchors, convert them to Polygons and rotate them.
Polygons anchors = to_polygons(this->_anchor_regions);
polygons_rotate(anchors, PI/2.0 - angle);
if (angle == -1)
angle = this->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);
/* 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));
if (angle != -1) {
// 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);
// Get anchors, convert them to Polygons and rotate them.
Polygons anchors = to_polygons(this->_anchor_regions);
polygons_rotate(anchors, PI/2.0 - angle);
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

View File

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

View File

@ -14,154 +14,202 @@ using ClipperLib::jtSquare;
namespace Slic3r {
// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library.
//FIXME Vojtech: Better to use a power of 2 coefficient and to use bit shifts for scaling.
// How about 2^17=131072?
// By the way, is the scalling needed at all? Cura runs all the computation with a fixed point precision of 1um, while Slic3r scales to 1nm,
// further scaling by 10e5 brings us to
#define CLIPPER_OFFSET_SCALE 100000.0
//-----------------------------------------------------------
// legacy code from Clipper documentation
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons);
void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons);
//-----------------------------------------------------------
void Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output);
template <class T>
void Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output);
template <class T>
void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output);
template <class T>
void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output);
void ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output);
void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale);
ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input);
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input);
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input);
Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input);
Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input);
Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input);
Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input);
Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input);
// offset Polygons
void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
// This is a safe variant of the polygon offset, tailored for a single ExPolygon:
// a single polygon with multiple non-overlapping holes.
// Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours.
void offset(const Slic3r::ExPolygon &expolygon, ClipperLib::Paths* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta,
double scale, ClipperLib::JoinType joinType, double miterLimit);
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta,
double scale, ClipperLib::JoinType joinType, double miterLimit);
ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
// offset Polylines
void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double miterLimit = 3);
void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double miterLimit = 3);
void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare,
double miterLimit = 3);
inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); }
inline Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); }
void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta,
double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
// offset expolygons and surfaces
ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit);
ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit);
inline Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
inline Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1,
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
template <class T>
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
const Slic3r::Polygons &clip, T* retval, bool safety_offset_);
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
const Slic3r::Polygons &clip, ClipperLib::Paths* retval, bool safety_offset_);
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_);
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_);
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
const Slic3r::Polygons &clip, Slic3r::Polylines* retval);
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject,
const Slic3r::Polygons &clip, Slic3r::Lines* retval);
Slic3r::Polygons _clipper(ClipperLib::ClipType clipType,
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType,
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType,
const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
template <class SubjectType, class ResultType>
void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false);
// diff
inline Slic3r::Polygons
diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
template <class SubjectType, class ResultType>
void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_ = false);
inline Slic3r::ExPolygons
diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
inline Slic3r::ExPolygons
diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
}
template <class SubjectType, class ClipType>
Slic3r::ExPolygons diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_ = false);
inline Slic3r::Polygons
diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
}
template <class SubjectType, class ResultType>
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false);
inline Slic3r::Polylines
diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
template <class SubjectType>
SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
inline Slic3r::Polylines
diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
Slic3r::ExPolygons
intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
inline Slic3r::Lines
diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_ln(ClipperLib::ctDifference, subject, clip, safety_offset_);
}
template <class SubjectType>
bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
// intersection
inline Slic3r::Polygons
intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval,
bool safety_offset_ = false);
inline Slic3r::ExPolygons
intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
template <class T>
void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_ = false);
inline Slic3r::ExPolygons
intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
}
Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset = false);
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset = false);
Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_offset = false);
inline Slic3r::Polygons
intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
}
void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset = false);
Slic3r::Polygons union_(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset = false);
inline Slic3r::Polylines
intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_ = false);
void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_ = false);
static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval);
inline Slic3r::Polylines
intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear = false);
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear = false);
inline Slic3r::Lines
intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
{
return _clipper_ln(ClipperLib::ctIntersection, subject, clip, safety_offset_);
}
// union
inline Slic3r::Polygons
union_(const Slic3r::Polygons &subject, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_);
}
inline Slic3r::Polygons
union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool safety_offset_ = false)
{
return _clipper(ClipperLib::ctUnion, subject, subject2, safety_offset_);
}
inline Slic3r::ExPolygons
union_ex(const Slic3r::Polygons &subject, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_);
}
inline Slic3r::ExPolygons
union_ex(const Slic3r::ExPolygons &subject, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
}
inline Slic3r::ExPolygons
union_ex(const Slic3r::Surfaces &subject, bool safety_offset_ = false)
{
return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
}
ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false);
Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false);
void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval);
/* OTHER */
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false);
Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false);
void safety_offset(ClipperLib::Paths* paths);
Polygons top_level_islands(const Slic3r::Polygons &polygons);
}
#endif

View File

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

View File

@ -1,6 +1,7 @@
#include <algorithm>
#include <vector>
#include <float.h>
#include <unordered_map>
#ifdef SLIC3R_GUI
#include <wx/image.h>
@ -109,7 +110,6 @@ void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resol
void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
{
// 1) Measure the bounding box.
m_bbox.defined = false;
for (size_t i = 0; i < m_contours.size(); ++ i) {
const Slic3r::Points &pts = *m_contours[i];
for (size_t j = 0; j < pts.size(); ++ j)
@ -839,6 +839,8 @@ void EdgeGrid::Grid::calculate_sdf()
}
#if 0
static int iRun = 0;
++ iRun;
//#ifdef SLIC3R_GUI
{
wxImage img(ncols, nrows);
@ -862,7 +864,7 @@ void EdgeGrid::Grid::calculate_sdf()
}
}
}
img.SaveFile(debug_out_path("unsigned_df.png"), wxBITMAP_TYPE_PNG);
img.SaveFile(debug_out_path("unsigned_df-%d.png", iRun), wxBITMAP_TYPE_PNG);
}
{
wxImage img(ncols, nrows);
@ -895,7 +897,7 @@ void EdgeGrid::Grid::calculate_sdf()
}
}
}
img.SaveFile(debug_out_path("signed_df.png"), wxBITMAP_TYPE_PNG);
img.SaveFile(debug_out_path("signed_df-%d.png", iRun), wxBITMAP_TYPE_PNG);
}
#endif /* SLIC3R_GUI */
@ -1020,7 +1022,7 @@ void EdgeGrid::Grid::calculate_sdf()
}
}
}
img.SaveFile(debug_out_path("signed_df-signs.png"), wxBITMAP_TYPE_PNG);
img.SaveFile(debug_out_path("signed_df-signs-%d.png", iRun), wxBITMAP_TYPE_PNG);
}
#endif /* SLIC3R_GUI */
@ -1049,7 +1051,7 @@ void EdgeGrid::Grid::calculate_sdf()
}
}
}
img.SaveFile(debug_out_path("signed_df2.png"), wxBITMAP_TYPE_PNG);
img.SaveFile(debug_out_path("signed_df2-%d.png", iRun), wxBITMAP_TYPE_PNG);
}
#endif /* SLIC3R_GUI */
}
@ -1222,6 +1224,135 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
return true;
}
Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
{
typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
// 0) Prepare a binary grid.
size_t cell_rows = m_rows + 2;
size_t cell_cols = m_cols + 2;
std::vector<char> cell_inside(cell_rows * cell_cols, false);
for (int r = 0; r < int(cell_rows); ++ r)
for (int c = 0; c < int(cell_cols); ++ c)
cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1);
// Fill in empty cells, which have a left / right neighbor filled.
// Fill in empty cells, which have the top / bottom neighbor filled.
{
std::vector<char> cell_inside2(cell_inside);
for (int r = 1; r + 1 < int(cell_rows); ++ r) {
for (int c = 1; c + 1 < int(cell_cols); ++ c) {
int addr = r * cell_cols + c;
if ((cell_inside2[addr - 1] && cell_inside2[addr + 1]) ||
(cell_inside2[addr - cell_cols] && cell_inside2[addr + cell_cols]))
cell_inside[addr] = true;
}
}
}
// 1) Collect the lines.
std::vector<Line> lines;
EndPointMapType start_point_to_line_idx;
for (int r = 0; r <= int(m_rows); ++ r) {
for (int c = 0; c <= int(m_cols); ++ c) {
int addr = (r + 1) * cell_cols + c + 1;
bool left = cell_inside[addr - 1];
bool top = cell_inside[addr - cell_cols];
bool current = cell_inside[addr];
if (left != current) {
lines.push_back(
left ?
Line(Point(c, r+1), Point(c, r )) :
Line(Point(c, r ), Point(c, r+1)));
start_point_to_line_idx.insert(std::pair<Point, int>(lines.back().a, int(lines.size()) - 1));
}
if (top != current) {
lines.push_back(
top ?
Line(Point(c , r), Point(c+1, r)) :
Line(Point(c+1, r), Point(c , r)));
start_point_to_line_idx.insert(std::pair<Point, int>(lines.back().a, int(lines.size()) - 1));
}
}
}
// 2) Chain the lines.
std::vector<char> line_processed(lines.size(), false);
Polygons out;
for (int i_candidate = 0; i_candidate < int(lines.size()); ++ i_candidate) {
if (line_processed[i_candidate])
continue;
Polygon poly;
line_processed[i_candidate] = true;
poly.points.push_back(lines[i_candidate].b);
int i_line_current = i_candidate;
for (;;) {
std::pair<EndPointMapType::iterator,EndPointMapType::iterator> line_range =
start_point_to_line_idx.equal_range(lines[i_line_current].b);
// The interval has to be non empty, there shall be at least one line continuing the current one.
assert(line_range.first != line_range.second);
int i_next = -1;
for (EndPointMapType::iterator it = line_range.first; it != line_range.second; ++ it) {
if (it->second == i_candidate) {
// closing the loop.
goto end_of_poly;
}
if (line_processed[it->second])
continue;
if (i_next == -1) {
i_next = it->second;
} else {
// This is a corner, where two lines meet exactly. Pick the line, which encloses a smallest angle with
// the current edge.
const Line &line_current = lines[i_line_current];
const Line &line_next = lines[it->second];
const Vector v1 = line_current.vector();
const Vector v2 = line_next.vector();
int64_t cross = int64_t(v1.x) * int64_t(v2.y) - int64_t(v2.x) * int64_t(v1.y);
if (cross > 0) {
// This has to be a convex right angle. There is no better next line.
i_next = it->second;
break;
}
}
}
line_processed[i_next] = true;
i_line_current = i_next;
poly.points.push_back(lines[i_line_current].b);
}
end_of_poly:
out.push_back(std::move(poly));
}
// 3) Scale the polygons back into world, shrink slightly and remove collinear points.
for (size_t i = 0; i < out.size(); ++ i) {
Polygon &poly = out[i];
for (size_t j = 0; j < poly.points.size(); ++ j) {
Point &p = poly.points[j];
p.x *= m_resolution;
p.y *= m_resolution;
p.x += m_bbox.min.x;
p.y += m_bbox.min.y;
}
// Shrink the contour slightly, so if the same contour gets discretized and simplified again, one will get the same result.
// Remove collineaer points.
Points pts;
pts.reserve(poly.points.size());
for (size_t j = 0; j < poly.points.size(); ++ j) {
size_t j0 = (j == 0) ? poly.points.size() - 1 : j - 1;
size_t j2 = (j + 1 == poly.points.size()) ? 0 : j + 1;
Point v = poly.points[j2] - poly.points[j0];
if (v.x != 0 && v.y != 0) {
// This is a corner point. Copy it to the output contour.
Point p = poly.points[j];
p.y += (v.x < 0) ? - offset : offset;
p.x += (v.y > 0) ? - offset : offset;
pts.push_back(p);
}
}
poly.points = std::move(pts);
}
return out;
}
#ifdef SLIC3R_GUI
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
{
@ -1235,17 +1366,18 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo
++iRun;
const coord_t search_radius = grid.resolution() * 2;
const coord_t display_blend_radius = grid.resolution() * 5;
const coord_t display_blend_radius = grid.resolution() * 2;
for (coord_t r = 0; r < h; ++r) {
for (coord_t c = 0; c < w; ++ c) {
unsigned char *pxl = data + (((h - r - 1) * w) + c) * 3;
Point pt(c * resolution + bbox.min.x, r * resolution + bbox.min.y);
coordf_t min_dist;
bool on_segment;
// if (grid.signed_distance_edges(pt, search_radius, min_dist, &on_segment)) {
bool on_segment = true;
#if 0
if (grid.signed_distance_edges(pt, search_radius, min_dist, &on_segment)) {
#else
if (grid.signed_distance(pt, search_radius, min_dist)) {
//FIXME
on_segment = true;
#endif
float s = 255 * std::abs(min_dist) / float(display_blend_radius);
int is = std::max(0, std::min(255, int(floor(s + 0.5f))));
if (min_dist < 0) {
@ -1254,8 +1386,8 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo
pxl[1] = 255 - is;
pxl[2] = 255 - is;
} else {
pxl[0] = 128;
pxl[1] = 128;
pxl[0] = 255;
pxl[1] = 0;
pxl[2] = 255 - is;
}
}

View File

@ -12,11 +12,14 @@
namespace Slic3r {
namespace EdgeGrid {
struct Grid
class Grid
{
public:
Grid();
~Grid();
void set_bbox(const BoundingBox &bbox) { m_bbox = bbox; }
void create(const Polygons &polygons, coord_t resolution);
void create(const ExPolygon &expoly, coord_t resolution);
void create(const ExPolygons &expolygons, coord_t resolution);
@ -54,6 +57,9 @@ struct Grid
const size_t rows() const { return m_rows; }
const size_t cols() const { return m_cols; }
// For supports: Contours enclosing the rasterized edges.
Polygons contours_simplified(coord_t offset) const;
protected:
struct Cell {
Cell() : begin(0), end(0) {}
@ -65,6 +71,18 @@ protected:
#if 0
bool line_cell_intersect(const Point &p1, const Point &p2, const Cell &cell);
#endif
bool cell_inside_or_crossing(int r, int c) const
{
if (r < 0 || r >= m_rows ||
c < 0 || c >= m_cols)
// The cell is outside the domain. Hoping that the contours were correctly oriented, so
// there is a CCW outmost contour so the out of domain cells are outside.
return false;
const Cell &cell = m_cells[r * m_cols + c];
return
(cell.begin < cell.end) ||
(! m_signed_distance_field.empty() && m_signed_distance_field[r * (m_cols + 1) + c] <= 0.f);
}
// Bounding box around the contours.
BoundingBox m_bbox;

View File

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

View File

@ -13,9 +13,17 @@ typedef std::vector<ExPolygon> ExPolygons;
class ExPolygon
{
public:
public:
ExPolygon() {}
ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {}
ExPolygon(ExPolygon &&other) : contour(std::move(other.contour)), holes(std::move(other.holes)) {}
ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; }
ExPolygon& operator=(ExPolygon &&other) { contour = std::move(other.contour); holes = std::move(other.holes); return *this; }
Polygon contour;
Polygons holes;
operator Points() const;
operator Polygons() const;
operator Polylines() const;
@ -253,6 +261,18 @@ inline void polygons_append(Polygons &dst, ExPolygons &&src)
}
#endif
inline void expolygons_append(ExPolygons &dst, const ExPolygons &src)
{
dst.insert(dst.end(), src.begin(), src.end());
}
#if SLIC3R_CPPVER >= 11
inline void expolygons_append(ExPolygons &dst, ExPolygons &&src)
{
std::move(std::begin(src), std::end(src), std::back_inserter(dst));
}
#endif
inline void expolygons_rotate(ExPolygons &expolys, double angle)
{
for (ExPolygons::iterator p = expolys.begin(); p != expolys.end(); ++p)
@ -281,37 +301,37 @@ extern bool remove_sticks(ExPolygon &poly);
#include <boost/polygon/polygon.hpp>
namespace boost { namespace polygon {
template <>
struct polygon_traits<ExPolygon> {
struct polygon_traits<Slic3r::ExPolygon> {
typedef coord_t coordinate_type;
typedef Points::const_iterator iterator_type;
typedef Point point_type;
typedef Slic3r::Points::const_iterator iterator_type;
typedef Slic3r::Point point_type;
// Get the begin iterator
static inline iterator_type begin_points(const ExPolygon& t) {
static inline iterator_type begin_points(const Slic3r::ExPolygon& t) {
return t.contour.points.begin();
}
// Get the end iterator
static inline iterator_type end_points(const ExPolygon& t) {
static inline iterator_type end_points(const Slic3r::ExPolygon& t) {
return t.contour.points.end();
}
// Get the number of sides of the polygon
static inline std::size_t size(const ExPolygon& t) {
static inline std::size_t size(const Slic3r::ExPolygon& t) {
return t.contour.points.size();
}
// Get the winding direction of the polygon
static inline winding_direction winding(const ExPolygon& t) {
static inline winding_direction winding(const Slic3r::ExPolygon& t) {
return unknown_winding;
}
};
template <>
struct polygon_mutable_traits<ExPolygon> {
struct polygon_mutable_traits<Slic3r::ExPolygon> {
//expects stl style iterators
template <typename iT>
static inline ExPolygon& set_points(ExPolygon& expolygon, iT input_begin, iT input_end) {
static inline Slic3r::ExPolygon& set_points(Slic3r::ExPolygon& expolygon, iT input_begin, iT input_end) {
expolygon.contour.points.assign(input_begin, input_end);
// skip last point since Boost will set last point = first point
expolygon.contour.points.pop_back();
@ -321,27 +341,27 @@ namespace boost { namespace polygon {
template <>
struct geometry_concept<ExPolygon> { typedef polygon_with_holes_concept type; };
struct geometry_concept<Slic3r::ExPolygon> { typedef polygon_with_holes_concept type; };
template <>
struct polygon_with_holes_traits<ExPolygon> {
typedef Polygons::const_iterator iterator_holes_type;
typedef Polygon hole_type;
static inline iterator_holes_type begin_holes(const ExPolygon& t) {
struct polygon_with_holes_traits<Slic3r::ExPolygon> {
typedef Slic3r::Polygons::const_iterator iterator_holes_type;
typedef Slic3r::Polygon hole_type;
static inline iterator_holes_type begin_holes(const Slic3r::ExPolygon& t) {
return t.holes.begin();
}
static inline iterator_holes_type end_holes(const ExPolygon& t) {
static inline iterator_holes_type end_holes(const Slic3r::ExPolygon& t) {
return t.holes.end();
}
static inline unsigned int size_holes(const ExPolygon& t) {
static inline unsigned int size_holes(const Slic3r::ExPolygon& t) {
return (int)t.holes.size();
}
};
template <>
struct polygon_with_holes_mutable_traits<ExPolygon> {
struct polygon_with_holes_mutable_traits<Slic3r::ExPolygon> {
template <typename iT>
static inline ExPolygon& set_holes(ExPolygon& t, iT inputBegin, iT inputEnd) {
static inline Slic3r::ExPolygon& set_holes(Slic3r::ExPolygon& t, iT inputBegin, iT inputEnd) {
t.holes.assign(inputBegin, inputEnd);
return t;
}
@ -349,32 +369,32 @@ namespace boost { namespace polygon {
//first we register CPolygonSet as a polygon set
template <>
struct geometry_concept<ExPolygons> { typedef polygon_set_concept type; };
struct geometry_concept<Slic3r::ExPolygons> { typedef polygon_set_concept type; };
//next we map to the concept through traits
template <>
struct polygon_set_traits<ExPolygons> {
struct polygon_set_traits<Slic3r::ExPolygons> {
typedef coord_t coordinate_type;
typedef ExPolygons::const_iterator iterator_type;
typedef ExPolygons operator_arg_type;
typedef Slic3r::ExPolygons::const_iterator iterator_type;
typedef Slic3r::ExPolygons operator_arg_type;
static inline iterator_type begin(const ExPolygons& polygon_set) {
static inline iterator_type begin(const Slic3r::ExPolygons& polygon_set) {
return polygon_set.begin();
}
static inline iterator_type end(const ExPolygons& polygon_set) {
static inline iterator_type end(const Slic3r::ExPolygons& polygon_set) {
return polygon_set.end();
}
//don't worry about these, just return false from them
static inline bool clean(const ExPolygons& polygon_set) { return false; }
static inline bool sorted(const ExPolygons& polygon_set) { return false; }
static inline bool clean(const Slic3r::ExPolygons& polygon_set) { return false; }
static inline bool sorted(const Slic3r::ExPolygons& polygon_set) { return false; }
};
template <>
struct polygon_set_mutable_traits<ExPolygons> {
struct polygon_set_mutable_traits<Slic3r::ExPolygons> {
template <typename input_iterator_type>
static inline void set(ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) {
static inline void set(Slic3r::ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) {
expolygons.assign(input_begin, input_end);
}
};

View File

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

View File

@ -69,11 +69,11 @@ class ExtrusionPath : public ExtrusionEntity
public:
Polyline polyline;
ExtrusionRole role;
// Volumetric velocity. mm^3 of plastic per mm of linear head motion
// Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator.
double mm3_per_mm;
// Width of the extrusion.
// Width of the extrusion, used for visualization purposes.
float width;
// Height of the extrusion.
// Height of the extrusion, used for visualization purposed.
float height;
ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {};
@ -194,6 +194,48 @@ class ExtrusionLoop : public ExtrusionEntity
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
};
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + polylines.size());
for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height));
dst.back().polyline = *it_polyline;
}
}
#if SLIC3R_CPPVER >= 11
inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + polylines.size());
for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height));
dst.back().polyline = std::move(*it_polyline);
}
}
#endif // SLIC3R_CPPVER >= 11
inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + polylines.size());
for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height);
dst.push_back(extrusion_path);
extrusion_path->polyline = *it_polyline;
}
}
#if SLIC3R_CPPVER >= 11
inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
{
dst.reserve(dst.size() + polylines.size());
for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height);
dst.push_back(extrusion_path);
extrusion_path->polyline = std::move(*it_polyline);
}
}
#endif // SLIC3R_CPPVER >= 11
}
#endif

View File

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

View File

@ -13,7 +13,7 @@ enum ExtrusionSimulationType
ExtrusionSimulationDontSpread,
ExtrisopmSimulationSpreadNotOverfilled,
ExtrusionSimulationSpreadFull,
ExtrusionSimulationSpreadExcess,
ExtrusionSimulationSpreadExcess
};
// An opaque class, to keep the boost stuff away from the header.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -44,7 +44,7 @@ void FillPlanePath::_fill_surface_single(
coord_t(floor(it->x * distance_between_lines + 0.5)),
coord_t(floor(it->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) {

View File

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

View File

@ -372,11 +372,9 @@ public:
bool sticks_removed = remove_sticks(polygons_src);
// if (sticks_removed) printf("Sticks removed!\n");
polygons_outer = offset(polygons_src, aoffset1,
CLIPPER_OFFSET_SCALE,
ClipperLib::jtMiter,
mitterLimit);
polygons_inner = offset(polygons_outer, aoffset2 - aoffset1,
CLIPPER_OFFSET_SCALE,
ClipperLib::jtMiter,
mitterLimit);
// Filter out contours with zero area or small area, contours with 2 points only.
@ -884,7 +882,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
Point refpt = rotate_vector.second.rotated(- rotate_vector.first);
// _align_to_grid will not work correctly with positive pattern_shift.
coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing;
refpt.x -= (pattern_shift_scaled > 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
refpt.x -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
bounding_box.merge(_align_to_grid(
bounding_box.min,
Point(line_spacing, line_spacing),
@ -894,7 +892,9 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
// Intersect a set of euqally spaced vertical lines wiht expolygon.
// n_vlines = ceil(bbox_width / line_spacing)
size_t n_vlines = (bounding_box.max.x - bounding_box.min.x + line_spacing - 1) / line_spacing;
coord_t x0 = bounding_box.min.x + (line_spacing + SCALED_EPSILON) / 2;
coord_t x0 = bounding_box.min.x;
if (full_infill)
x0 += (line_spacing + SCALED_EPSILON) / 2;
#ifdef SLIC3R_DEBUG
static int iRun = 0;

View File

@ -40,14 +40,17 @@ Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool
/* This method returns the centerline spacing between two adjacent extrusions
having the same extrusion width (and other properties). */
float
Flow::spacing() const {
if (this->bridge) {
Flow::spacing() const
{
#ifdef HAS_PERIMETER_LINE_OVERLAP
if (this->bridge)
return this->width + BRIDGE_EXTRA_SPACING;
}
// rectangle with semicircles at the ends
float min_flow_spacing = this->width - this->height * (1 - PI/4.0);
return this->width - OVERLAP_FACTOR * (this->width - min_flow_spacing);
return this->width - PERIMETER_LINE_OVERLAP_FACTOR * (this->width - min_flow_spacing);
#else
return this->bridge ? (this->width + BRIDGE_EXTRA_SPACING) : (this->width - this->height * (1 - PI/4.0));
#endif
}
/* This method returns the centerline spacing between an extrusion using this
@ -57,23 +60,17 @@ float
Flow::spacing(const Flow &other) const {
assert(this->height == other.height);
assert(this->bridge == other.bridge);
if (this->bridge) {
return this->width/2 + other.width/2 + BRIDGE_EXTRA_SPACING;
}
return this->spacing()/2 + other.spacing()/2;
return this->bridge ?
0.5f * this->width + 0.5f * other.width + BRIDGE_EXTRA_SPACING :
0.5f * this->spacing() + 0.5f * other.spacing();
}
/* This method returns extrusion volume per head move unit. */
double
Flow::mm3_per_mm() const {
if (this->bridge) {
return (this->width * this->width) * PI/4.0;
}
// rectangle with semicircles at the ends
return this->width * this->height + (this->height*this->height) / 4.0 * (PI-4.0);
double Flow::mm3_per_mm() const
{
return this->bridge ?
(this->width * this->width) * PI/4.0 :
this->width * this->height + (this->height * this->height) / 4.0 * (PI-4.0);
}
/* This static method returns bridge width for a given nozzle diameter. */
@ -85,8 +82,7 @@ float Flow::_bridge_width(float nozzle_diameter, float bridge_flow_ratio) {
}
/* This static method returns a sane extrusion width default. */
float
Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) {
float Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) {
// here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate
// shape: rectangle with semicircles at the ends
float width = ((nozzle_diameter*nozzle_diameter) * PI + (height*height) * (4.0 - PI)) / (4.0 * height);
@ -106,14 +102,15 @@ Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) {
}
/* This static method returns the extrusion width value corresponding to the supplied centerline spacing. */
float
Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) {
if (bridge) {
return spacing - BRIDGE_EXTRA_SPACING;
}
// rectangle with semicircles at the ends
return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0);
float Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge)
{
return bridge ?
(spacing - BRIDGE_EXTRA_SPACING) :
#ifdef HAS_PERIMETER_LINE_OVERLAP
(spacing + PERIMETER_LINE_OVERLAP_FACTOR * height * (1 - PI/4.0));
#else
(spacing + height * (1 - PI/4.0));
#endif
}
}

View File

@ -7,8 +7,14 @@
namespace Slic3r {
// Extra spacing of bridge threads, in mm.
#define BRIDGE_EXTRA_SPACING 0.05
#define OVERLAP_FACTOR 1.0
// Overlap factor of perimeter lines. Currently no overlap.
// #define HAS_OVERLAP
#ifdef HAS_PERIMETER_LINE_OVERLAP
#define PERIMETER_LINE_OVERLAP_FACTOR 1.0
#endif
enum FlowRole {
frExternalPerimeter,
@ -22,9 +28,17 @@ enum FlowRole {
class Flow
{
public:
float width, height, nozzle_diameter;
bool bridge;
public:
// Non bridging flow: Maximum width of an extrusion with semicircles at the ends.
// Bridging flow: Bridge thread diameter.
float width;
// Non bridging flow: Layer height.
// Bridging flow: Bridge thread diameter = layer height.
float height;
// Nozzle diameter is
float nozzle_diameter;
// Is it a bridge?
bool bridge;
Flow(float _w, float _h, float _nd, bool _bridge = false)
: width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {};

View File

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

View File

@ -105,7 +105,7 @@ Layer::make_slices()
FOREACH_LAYERREGION(this, layerm) {
polygons_append(slices_p, to_polygons((*layerm)->slices));
}
union_(slices_p, &slices);
slices = union_ex(slices_p);
}
this->slices.expolygons.clear();
@ -132,15 +132,11 @@ Layer::merge_slices()
if (this->regions.size() == 1) {
// Optimization, also more robust. Don't merge classified pieces of layerm->slices,
// but use the non-split islands of a layer. For a single region print, these shall be equal.
this->regions.front()->slices.surfaces.clear();
surfaces_append(this->regions.front()->slices.surfaces, this->slices.expolygons, stInternal);
this->regions.front()->slices.set(this->slices.expolygons, stInternal);
} else {
FOREACH_LAYERREGION(this, layerm) {
ExPolygons expp;
// without safety offset, artifacts are generated (GH #2494)
union_(to_polygons(STDMOVE((*layerm)->slices.surfaces)), &expp, true);
(*layerm)->slices.surfaces.clear();
surfaces_append((*layerm)->slices.surfaces, expp, stInternal);
(*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal);
}
}
}
@ -223,7 +219,7 @@ Layer::make_perimeters()
}
// merge the surfaces assigned to each group
for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it)
surfaces_append(new_slices.surfaces, union_ex(it->second, true), it->second.front());
new_slices.append(union_ex(it->second, true), it->second.front());
}
// make perimeters
@ -236,8 +232,7 @@ Layer::make_perimeters()
// Separate the fill surfaces.
ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices);
(*l)->fill_expolygons = expp;
(*l)->fill_surfaces.surfaces.clear();
surfaces_append((*l)->fill_surfaces.surfaces, STDMOVE(expp), fill_surfaces.surfaces.front());
(*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front());
}
}
}
@ -318,9 +313,4 @@ SupportLayer::SupportLayer(size_t id, PrintObject *object, coordf_t height,
{
}
SupportLayer::~SupportLayer()
{
}
}

View File

@ -11,9 +11,6 @@
namespace Slic3r {
typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
class Layer;
class PrintRegion;
class PrintObject;
@ -155,7 +152,7 @@ public:
protected:
SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z,
coordf_t slice_z);
virtual ~SupportLayer();
virtual ~SupportLayer() {}
};

View File

@ -52,8 +52,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
Polygons fill_boundaries = to_polygons(this->fill_expolygons);
this->fill_surfaces.surfaces.clear();
for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++ surface)
surfaces_append(
this->fill_surfaces.surfaces,
this->fill_surfaces.append(
intersection_ex(to_polygons(surface->expolygon), fill_boundaries),
surface->surface_type);
}
@ -91,9 +90,9 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection*
g.process();
}
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 3.
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 1.5
#define EXTERNAL_SURFACES_OFFSET_PARAMETERS CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare, 0.
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
void
LayerRegion::process_external_surfaces(const Layer* lower_layer)
@ -194,7 +193,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
break;
}
// Grown by 3mm.
Polygons polys = offset(bridges[i].expolygon, float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS);
Polygons polys = offset(to_polygons(bridges[i].expolygon), float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS);
if (idx_island == -1) {
printf("Bridge did not fall into the source region!\r\n");
} else {
@ -262,9 +261,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
BridgeDetector bd(
initial,
lower_layer->slices,
//FIXME parameters are not correct!
// flow(FlowRole role, bool bridge = false, double width = -1) const;
this->flow(frInfill, true, this->layer()->height).scaled_width()
this->flow(frInfill, true).scaled_width()
);
#ifdef SLIC3R_DEBUG
printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id());
@ -305,8 +302,10 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
polygons_append(polys, STDMOVE(s1));
for (size_t j = i + 1; j < top.size(); ++ j) {
Surface &s2 = top[j];
if (! s2.empty() && surfaces_could_merge(s1, s2))
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, STDMOVE(s2));
s2.clear();
}
}
if (s1.surface_type == stTop)
// Trim the top surfaces by the bottom surfaces. This gives the priority to the bottom surfaces.
@ -329,8 +328,10 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
polygons_append(polys, STDMOVE(s1));
for (size_t j = i + 1; j < internal.size(); ++ j) {
Surface &s2 = internal[j];
if (! s2.empty() && surfaces_could_merge(s1, s2))
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, STDMOVE(s2));
s2.clear();
}
}
ExPolygons new_expolys = diff_ex(polys, new_polygons);
polygons_append(new_polygons, to_polygons(new_expolys));

View File

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

View File

@ -6,6 +6,7 @@
#include "Layer.hpp"
#include "Point.hpp"
#include "TriangleMesh.hpp"
#include "Slicing.hpp"
#include <map>
#include <string>
#include <utility>

View File

@ -142,7 +142,7 @@ MotionPlanner::shortest_path(const Point &from, const Point &to)
{
// grow our environment slightly in order for simplify_by_visibility()
// 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 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -133,12 +133,6 @@ Print::clear_regions()
this->delete_region(i);
}
PrintRegion*
Print::get_region(size_t idx)
{
return regions.at(idx);
}
PrintRegion*
Print::add_region()
{
@ -608,20 +602,15 @@ Print::validate() const
object->model_object()->instances.front()->transform_polygon(&convex_hull);
// grow convex hull with the clearance margin
{
Polygons grown_hull;
offset(convex_hull, &grown_hull, scale_(this->config.extruder_clearance_radius.value)/2, 1, jtRound, scale_(0.1));
convex_hull = grown_hull.front();
}
convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front();
// now we check that no instance of convex_hull intersects any of the previously checked object instances
for (Points::const_iterator copy = object->_shifted_copies.begin(); copy != object->_shifted_copies.end(); ++copy) {
Polygon p = convex_hull;
p.translate(*copy);
if (intersects(a, p))
if (! intersection(a, p).empty())
return "Some objects are too close; your extruder will collide with them.";
union_(a, p, &a);
polygons_append(a, p);
}
}
}

View File

@ -12,7 +12,7 @@
#include "Layer.hpp"
#include "Model.hpp"
#include "PlaceholderParser.hpp"
#include "Slicing.hpp"
namespace Slic3r {
@ -79,6 +79,10 @@ public:
PrintObjectConfig config;
t_layer_height_ranges layer_height_ranges;
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
std::vector<coordf_t> layer_height_profile;
// this is set to true when LayerRegion->slices is split in top/internal/bottom
// so that next call to make_perimeters() performs a union() before computing loops
bool typed_slices;
@ -137,11 +141,25 @@ public:
bool invalidate_step(PrintObjectStep step);
bool invalidate_all_steps();
// Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile.
// The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces.
void update_layer_height_profile();
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
// by the interactive layer height editor and by the printing process itself.
// The slicing parameters are dependent on various configuration values
// (layer height, first layer height, raft settings, print nozzle diameter etc).
SlicingParameters slicing_parameters() const;
void _slice();
bool has_support_material() const;
void detect_surfaces_type();
void process_external_surfaces();
void discover_vertical_shells();
void bridge_over_infill();
void _make_perimeters();
void _infill();
void _generate_support_material();
private:
Print* _print;
@ -152,6 +170,8 @@ private:
// parameter
PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox);
~PrintObject() {}
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
};
typedef std::vector<PrintObject*> PrintObjectPtrs;
@ -186,7 +206,8 @@ class Print
bool reload_model_instances();
// methods for handling regions
PrintRegion* get_region(size_t idx);
PrintRegion* get_region(size_t idx) { return regions.at(idx); }
const PrintRegion* get_region(size_t idx) const { return regions.at(idx); }
PrintRegion* add_region();
// methods for handling state

View File

@ -1,4 +1,5 @@
#include "PrintConfig.hpp"
#include <boost/thread.hpp>
namespace Slic3r {
@ -1120,6 +1121,17 @@ PrintConfigDef::PrintConfigDef()
def->cli = "support-material!";
def->default_value = new ConfigOptionBool(false);
def = this->add("support_material_xy_spacing", coFloatOrPercent);
def->label = "XY separation between an object and its support";
def->category = "Support material";
def->tooltip = "XY separation between an object and its support. If expressed as percentage (for example 50%), it will be calculated over external perimeter width.";
def->sidetext = "mm or %";
def->cli = "support-material-xy-spacing=s";
def->ratio_over = "external_perimeter_extrusion_width";
def->min = 0;
// Default is half the external perimeter width.
def->default_value = new ConfigOptionFloatOrPercent(50, true);
def = this->add("support_material_angle", coInt);
def->label = "Pattern angle";
def->category = "Support material";
@ -1177,6 +1189,13 @@ PrintConfigDef::PrintConfigDef()
def->cli = "support-material-extrusion-width=s";
def->default_value = new ConfigOptionFloatOrPercent(0, false);
def = this->add("support_material_interface_contact_loops", coBool);
def->label = "Interface circles";
def->category = "Support material";
def->tooltip = "Cover the top most interface layer with contact loops";
def->cli = "support-material-interface-contact-loops!";
def->default_value = new ConfigOptionBool(true);
def = this->add("support_material_interface_extruder", coInt);
def->label = "Support material/raft interface extruder";
def->category = "Extruders";
@ -1247,6 +1266,13 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloat(60);
def = this->add("support_material_synchronize_layers", coBool);
def->label = "Synchronize with object layers";
def->category = "Support material";
def->tooltip = "Synchronize support layers with the object print layers. This is useful with multi-material printers, where the extruder switch is expensive.";
def->cli = "support-material-synchronize-layers!";
def->default_value = new ConfigOptionBool(false);
def = this->add("support_material_threshold", coInt);
def->label = "Overhang threshold";
def->category = "Support material";
@ -1290,8 +1316,10 @@ PrintConfigDef::PrintConfigDef()
def->cli = "threads|j=i";
def->readonly = true;
def->min = 1;
def->max = 16;
def->default_value = new ConfigOptionInt(2);
{
unsigned int threads = boost::thread::hardware_concurrency();
def->default_value = new ConfigOptionInt(threads > 0 ? threads : 2);
}
def = this->add("toolchange_gcode", coString);
def->label = "Tool change G-code";

View File

@ -153,6 +153,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
ConfigOptionInt support_material_enforce_layers;
ConfigOptionInt support_material_extruder;
ConfigOptionFloatOrPercent support_material_extrusion_width;
ConfigOptionBool support_material_interface_contact_loops;
ConfigOptionInt support_material_interface_extruder;
ConfigOptionInt support_material_interface_layers;
ConfigOptionFloat support_material_interface_spacing;
@ -160,8 +161,10 @@ class PrintObjectConfig : public virtual StaticPrintConfig
ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
ConfigOptionFloat support_material_spacing;
ConfigOptionFloat support_material_speed;
ConfigOptionBool support_material_synchronize_layers;
ConfigOptionInt support_material_threshold;
ConfigOptionBool support_material_with_sheath;
ConfigOptionFloatOrPercent support_material_xy_spacing;
ConfigOptionFloat xy_size_compensation;
PrintObjectConfig(bool initialize = true) : StaticPrintConfig() {
@ -185,6 +188,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
OPT_PTR(support_material_buildplate_only);
OPT_PTR(support_material_contact_distance);
OPT_PTR(support_material_enforce_layers);
OPT_PTR(support_material_interface_contact_loops);
OPT_PTR(support_material_extruder);
OPT_PTR(support_material_extrusion_width);
OPT_PTR(support_material_interface_extruder);
@ -194,6 +198,8 @@ class PrintObjectConfig : public virtual StaticPrintConfig
OPT_PTR(support_material_pattern);
OPT_PTR(support_material_spacing);
OPT_PTR(support_material_speed);
OPT_PTR(support_material_synchronize_layers);
OPT_PTR(support_material_xy_spacing);
OPT_PTR(support_material_threshold);
OPT_PTR(support_material_with_sheath);
OPT_PTR(xy_size_compensation);

View File

@ -2,10 +2,28 @@
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include "SVG.hpp"
#include "SupportMaterial.hpp"
#include <boost/log/trivial.hpp>
#include <Shiny/Shiny.h>
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
#define SLIC3R_DEBUG
#endif
// #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
#ifdef SLIC3R_DEBUG
#undef NDEBUG
#define DEBUG
#define _DEBUG
#include "SVG.hpp"
#undef assert
#include <cassert>
#endif
namespace Slic3r {
PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox)
@ -115,8 +133,12 @@ PrintObject::layer_count() const
void
PrintObject::clear_layers()
{
for (int i = this->layers.size()-1; i >= 0; --i)
this->delete_layer(i);
for (size_t i = 0; i < this->layers.size(); ++ i) {
Layer *layer = this->layers[i];
layer->upper_layer = layer->lower_layer = nullptr;
delete layer;
}
this->layers.clear();
}
Layer*
@ -144,8 +166,12 @@ PrintObject::support_layer_count() const
void
PrintObject::clear_support_layers()
{
for (int i = this->support_layers.size()-1; i >= 0; --i)
this->delete_support_layer(i);
for (size_t i = 0; i < this->support_layers.size(); ++ i) {
Layer *layer = this->support_layers[i];
layer->upper_layer = layer->lower_layer = nullptr;
delete layer;
}
this->support_layers.clear();
}
SupportLayer*
@ -197,12 +223,15 @@ PrintObject::invalidate_state_by_config_options(const std::vector<t_config_optio
|| *opt_key == "support_material_extruder"
|| *opt_key == "support_material_extrusion_width"
|| *opt_key == "support_material_interface_layers"
|| *opt_key == "support_material_interface_contact_loops"
|| *opt_key == "support_material_interface_extruder"
|| *opt_key == "support_material_interface_spacing"
|| *opt_key == "support_material_interface_speed"
|| *opt_key == "support_material_buildplate_only"
|| *opt_key == "support_material_pattern"
|| *opt_key == "support_material_xy_spacing"
|| *opt_key == "support_material_spacing"
|| *opt_key == "support_material_synchronize_layers"
|| *opt_key == "support_material_threshold"
|| *opt_key == "support_material_with_sheath"
|| *opt_key == "dont_support_bridges"
@ -323,7 +352,8 @@ PrintObject::has_support_material() const
// If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins.
void PrintObject::detect_surfaces_type()
{
// Slic3r::debugf "Detecting solid surfaces...\n";
BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces...";
for (int idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) {
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (int idx_layer = 0; idx_layer < int(this->layer_count()); ++ idx_layer) {
@ -435,7 +465,7 @@ void PrintObject::detect_surfaces_type()
{
Polygons topbottom = to_polygons(top);
polygons_append(topbottom, to_polygons(bottom));
surfaces_append(layerm->slices.surfaces,
layerm->slices.append(
#if 0
offset2_ex(diff(layerm_slices_surfaces, topbottom, true), -offset, offset),
#else
@ -444,8 +474,8 @@ void PrintObject::detect_surfaces_type()
stInternal);
}
surfaces_append(layerm->slices.surfaces, STDMOVE(top));
surfaces_append(layerm->slices.surfaces, STDMOVE(bottom));
layerm->slices.append(STDMOVE(top));
layerm->slices.append(STDMOVE(bottom));
// Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n",
// $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug;
@ -469,6 +499,8 @@ void PrintObject::detect_surfaces_type()
void
PrintObject::process_external_surfaces()
{
BOOST_LOG_TRIVIAL(info) << "Processing external surfaces...";
FOREACH_REGION(this->_print, region) {
size_t region_id = region - this->_print->regions.begin();
@ -497,6 +529,8 @@ PrintObject::discover_vertical_shells()
{
PROFILE_FUNC();
BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells...";
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
for (size_t idx_region = 0; idx_region < this->_print->regions.size(); ++ idx_region) {
@ -518,8 +552,19 @@ PrintObject::discover_vertical_shells()
{
PROFILE_BLOCK(discover_vertical_shells_region_layer);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
static size_t debug_idx = 0;
++ debug_idx;
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
Layer *layer = this->layers[idx_layer];
LayerRegion *layerm = layer->get_region(idx_region);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-initial");
layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-initial");
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
Flow solid_infill_flow = layerm->flow(frSolidInfill);
coord_t infill_line_spacing = solid_infill_flow.scaled_spacing();
// Find a union of perimeters below / above this surface to guarantee a minimum shell thickness.
@ -532,16 +577,16 @@ PrintObject::discover_vertical_shells()
if (1)
{
PROFILE_BLOCK(discover_vertical_shells_region_layer_collect);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
#if 0
// #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", idx), this->bounding_box());
Slic3r::SVG svg_cummulative(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d.svg", debug_idx), this->bounding_box());
for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) {
if (n < 0 || n >= (int)this->layers.size())
continue;
ExPolygons &expolys = this->layers[n]->perimeter_expolygons;
for (size_t i = 0; i < expolys.size(); ++ i) {
SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", idx, n, i), get_extents(expolys[i]));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-run%d-layer%d-expoly%d.svg", debug_idx, n, i), get_extents(expolys[i]));
svg.draw(expolys[i]);
svg.draw_outline(expolys[i].contour, "black", scale_(0.05));
svg.draw_outline(expolys[i].holes, "blue", scale_(0.05));
@ -552,7 +597,6 @@ PrintObject::discover_vertical_shells()
svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05));
}
}
++ idx;
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Reset the top / bottom inflated regions caches of entries, which are out of the moving window.
@ -610,8 +654,7 @@ PrintObject::discover_vertical_shells()
}
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", idx ++), get_extents(shell));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell));
svg.draw(shell);
svg.draw_outline(shell, "black", scale_(0.05));
svg.Close();
@ -634,8 +677,7 @@ PrintObject::discover_vertical_shells()
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", idx ++), get_extents(shell));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell));
svg.draw(shell_ex);
svg.draw_outline(shell_ex, "black", "blue", scale_(0.05));
svg.Close();
@ -644,8 +686,7 @@ PrintObject::discover_vertical_shells()
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", idx ++), get_extents(shell));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internal-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces.filter_by_type(stInternal), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternal), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
@ -653,8 +694,7 @@ PrintObject::discover_vertical_shells()
svg.Close();
}
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
@ -662,8 +702,7 @@ PrintObject::discover_vertical_shells()
svg.Close();
}
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", idx ++), get_extents(shell));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell));
svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5);
svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05));
svg.draw(shell_ex, "blue", 0.5);
@ -674,7 +713,7 @@ PrintObject::discover_vertical_shells()
// Trim the shells region by the internal & internal void surfaces.
const SurfaceType surfaceTypesInternal[] = { stInternal, stInternalVoid, stInternalSolid };
const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 2));
const Polygons polygonsInternal = to_polygons(layerm->fill_surfaces.filter_by_types(surfaceTypesInternal, 3));
shell = intersection(shell, polygonsInternal, true);
polygons_append(shell, diff(polygonsInternal, holes));
if (shell.empty())
@ -692,8 +731,7 @@ PrintObject::discover_vertical_shells()
#if 1
// Intentionally inflate a bit more than how much the region has been shrunk,
// so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing,
CLIPPER_OFFSET_SCALE, ClipperLib::jtSquare);
shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare);
if (shell.empty())
continue;
#else
@ -705,7 +743,7 @@ PrintObject::discover_vertical_shells()
// get a triangle in $too_narrow; if we grow it below then the shell
// would have a different shape from the external surface and we'd still
// have the same angle, so the next shell would be grown even more and so on.
Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, CLIPPER_OFFSET_SCALE, ClipperLib::jtMiter, 5.), true);
Polygons too_narrow = diff(shell, offset2(shell, -margin, margin, ClipperLib::jtMiter, 5.), true);
if (! too_narrow.empty()) {
// grow the collapsing parts and add the extra area to the neighbor layer
// as well as to our original surfaces so that we support this
@ -717,8 +755,7 @@ PrintObject::discover_vertical_shells()
ExPolygons new_internal_solid = intersection_ex(polygonsInternal, shell, false);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", idx ++), get_extents(shell_before));
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-regularized-%d.svg", debug_idx), get_extents(shell_before));
// Source shell.
svg.draw(union_ex(shell_before, true));
// Shell trimmed to the internal surfaces.
@ -743,26 +780,27 @@ PrintObject::discover_vertical_shells()
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
static size_t idx = 0;
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal-%d.svg", idx), get_extents(shell), new_internal, "black", "blue", scale_(0.05));
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_void-%d.svg", idx), get_extents(shell), new_internal_void, "black", "blue", scale_(0.05));
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_solid-%d.svg", idx), get_extents(shell), new_internal_solid, "black", "blue", scale_(0.05));
++ idx;
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal-%d.svg", debug_idx), get_extents(shell), new_internal, "black", "blue", scale_(0.05));
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_void-%d.svg", debug_idx), get_extents(shell), new_internal_void, "black", "blue", scale_(0.05));
SVG::export_expolygons(debug_out_path("discover_vertical_shells-new_internal_solid-%d.svg", debug_idx), get_extents(shell), new_internal_solid, "black", "blue", scale_(0.05));
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
// Assign resulting internal surfaces to layer.
const SurfaceType surfaceTypesKeep[] = { stTop, stBottom, stBottomBridge };
layerm->fill_surfaces.keep_types(surfaceTypesKeep, sizeof(surfaceTypesKeep)/sizeof(SurfaceType));
layerm->fill_surfaces.append(stInternal , new_internal);
layerm->fill_surfaces.append(stInternalVoid , new_internal_void);
layerm->fill_surfaces.append(stInternalSolid, new_internal_solid);
layerm->fill_surfaces.append(new_internal, stInternal);
layerm->fill_surfaces.append(new_internal_void, stInternalVoid);
layerm->fill_surfaces.append(new_internal_solid, stInternalSolid);
} // for each layer
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells");
layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells");
for (size_t idx_layer = 0; idx_layer < this->layers.size(); ++idx_layer) {
LayerRegion *layerm = this->layers[idx_layer]->get_region(idx_region);
layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells-final");
layerm->export_region_fill_surfaces_to_svg_debug("4_discover_vertical_shells-final");
}
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} // for each layer
} // for each region
// Write the profiler measurements to file
@ -775,6 +813,8 @@ PrintObject::discover_vertical_shells()
void
PrintObject::bridge_over_infill()
{
BOOST_LOG_TRIVIAL(info) << "Bridge over infill...";
FOREACH_REGION(this->_print, region) {
size_t region_id = region - this->_print->regions.begin();
@ -846,7 +886,7 @@ PrintObject::bridge_over_infill()
#endif
// compute the remaning internal solid surfaces as difference
ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true);
ExPolygons not_to_bridge = diff_ex(internal_solid, to_polygons(to_bridge), true);
to_bridge = intersection_ex(to_polygons(to_bridge), internal_solid, true);
// build the new collection of fill_surfaces
@ -902,4 +942,332 @@ PrintObject::bridge_over_infill()
}
}
SlicingParameters PrintObject::slicing_parameters() const
{
return SlicingParameters::create_from_config(
this->print()->config, this->config,
unscale(this->size.z), this->print()->object_extruders());
}
void PrintObject::update_layer_height_profile()
{
if (this->layer_height_profile.empty()) {
if (0)
// if (this->layer_height_profile.empty())
this->layer_height_profile = layer_height_profile_adaptive(this->slicing_parameters(), this->layer_height_ranges,
this->model_object()->volumes);
else
this->layer_height_profile = layer_height_profile_from_ranges(this->slicing_parameters(), this->layer_height_ranges);
}
}
// 1) Decides Z positions of the layers,
// 2) Initializes layers and their regions
// 3) Slices the object meshes
// 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
// 5) Applies size compensation (offsets the slices in XY plane)
// 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
// Resulting expolygons of layer regions are marked as Internal.
//
// this should be idempotent
void PrintObject::_slice()
{
SlicingParameters slicing_params = this->slicing_parameters();
// 1) Initialize layers and their slice heights.
std::vector<float> slice_zs;
{
this->clear_layers();
// Object layers (pairs of bottom/top Z coordinate), without the raft.
this->update_layer_height_profile();
std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, this->layer_height_profile);
// Reserve object layers for the raft. Last layer of the raft is the contact layer.
int id = int(slicing_params.raft_layers());
slice_zs.reserve(object_layers.size());
Layer *prev = nullptr;
for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) {
coordf_t lo = object_layers[i_layer];
coordf_t hi = object_layers[i_layer + 1];
coordf_t slice_z = 0.5 * (lo + hi);
Layer *layer = this->add_layer(id ++, hi - lo, hi + slicing_params.object_print_z_min, slice_z);
slice_zs.push_back(float(slice_z));
if (prev != nullptr) {
prev->upper_layer = layer;
layer->lower_layer = prev;
}
// Make sure all layers contain layer region objects for all regions.
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id)
layer->add_region(this->print()->regions[region_id]);
prev = layer;
}
}
if (this->print()->regions.size() == 1) {
// Optimized for a single region. Slice the single non-modifier mesh.
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(0, slice_zs, false);
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
this->layers[layer_id]->regions.front()->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal);
} else {
// Slice all non-modifier volumes.
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
this->layers[layer_id]->regions[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal);
}
// Slice all modifier volumes.
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) {
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true);
// loop through the other regions and 'steal' the slices belonging to this one
for (size_t other_region_id = 0; other_region_id < this->print()->regions.size(); ++ other_region_id) {
if (region_id == other_region_id)
continue;
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) {
Layer *layer = layers[layer_id];
LayerRegion *layerm = layer->regions[region_id];
LayerRegion *other_layerm = layer->regions[other_region_id];
if (layerm == nullptr || other_layerm == nullptr)
continue;
Polygons other_slices = to_polygons(other_layerm->slices);
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
if (my_parts.empty())
continue;
// Remove such parts from original region.
other_layerm->slices.set(diff_ex(other_slices, to_polygons(my_parts)), stInternal);
// Append new parts to our region.
layerm->slices.append(std::move(my_parts), stInternal);
}
}
}
}
// remove last layer(s) if empty
while (! this->layers.empty()) {
const Layer *layer = this->layers.back();
for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id)
if (layer->regions[region_id] != nullptr && ! layer->regions[region_id]->slices.empty())
// Non empty layer.
goto end;
this->delete_layer(int(this->layers.size()) - 1);
}
end:
;
for (size_t layer_id = 0; layer_id < layers.size(); ++ layer_id) {
Layer *layer = this->layers[layer_id];
// apply size compensation
if (this->config.xy_size_compensation.value != 0.) {
float delta = float(scale_(this->config.xy_size_compensation.value));
if (layer->regions.size() == 1) {
// single region
LayerRegion *layerm = layer->regions.front();
layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stInternal);
} else {
if (delta < 0) {
// multiple regions, shrinking
// we apply the offset to the combined shape, then intersect it
// with the original slices for each region
Polygons region_slices;
for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id)
polygons_append(region_slices, layer->regions[region_id]->slices.surfaces);
Polygons slices = offset(union_(region_slices), delta);
for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) {
LayerRegion *layerm = layer->regions[region_id];
layerm->slices.set(std::move(intersection_ex(slices, to_polygons(std::move(layerm->slices.surfaces)))), stInternal);
}
} else {
// multiple regions, growing
// this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap
// so we give priority to the first one and so on
Polygons processed;
for (size_t region_id = 0;; ++ region_id) {
LayerRegion *layerm = layer->regions[region_id];
ExPolygons slices = offset_ex(to_expolygons(layerm->slices.surfaces), delta);
if (region_id > 0)
// Trim by the slices of already processed regions.
slices = diff_ex(to_polygons(std::move(slices)), processed);
if (region_id + 1 == layer->regions.size()) {
layerm->slices.set(std::move(slices), stInternal);
break;
}
polygons_append(processed, slices);
layerm->slices.set(std::move(slices), stInternal);
}
}
}
}
// Merge all regions' slices to get islands, chain them by a shortest path.
layer->make_slices();
}
}
std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
{
std::vector<ExPolygons> layers;
assert(region_id < this->region_volumes.size());
std::vector<int> &volumes = this->region_volumes[region_id];
if (! volumes.empty()) {
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh;
for (std::vector<int>::const_iterator it_volume = volumes.begin(); it_volume != volumes.end(); ++ it_volume) {
ModelVolume *volume = this->model_object()->volumes[*it_volume];
if (volume->modifier == modifier)
mesh.merge(volume->mesh);
}
if (mesh.stl.stats.number_of_facets > 0) {
// transform mesh
// we ignore the per-instance transformations currently and only
// consider the first one
this->model_object()->instances.front()->transform_mesh(&mesh, true);
// align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
mesh.translate(- unscale(this->_copies_shift.x), - unscale(this->_copies_shift.y), -this->model_object()->bounding_box().min.z);
// perform actual slicing
TriangleMeshSlicer mslicer(&mesh);
mslicer.slice(z, &layers);
}
}
return layers;
}
void
PrintObject::_make_perimeters()
{
if (this->state.is_done(posPerimeters)) return;
this->state.set_started(posPerimeters);
// merge slices if they were split into types
if (this->typed_slices) {
FOREACH_LAYER(this, layer_it)
(*layer_it)->merge_slices();
this->typed_slices = false;
this->state.invalidate(posPrepareInfill);
}
// compare each layer to the one below, and mark those slices needing
// one additional inner perimeter, like the top of domed objects-
// this algorithm makes sure that at least one perimeter is overlapping
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
// inside the object - infill_only_where_needed should be the method of choice for printing
// hollow objects
FOREACH_REGION(this->_print, region_it) {
size_t region_id = region_it - this->_print->regions.begin();
const PrintRegion &region = **region_it;
if (!region.config.extra_perimeters
|| region.config.perimeters == 0
|| region.config.fill_density == 0
|| this->layer_count() < 2) continue;
for (int i = 0; i < int(this->layer_count()) - 1; ++i) {
LayerRegion &layerm = *this->get_layer(i)->get_region(region_id);
const LayerRegion &upper_layerm = *this->get_layer(i+1)->get_region(region_id);
const Polygons upper_layerm_polygons = upper_layerm.slices;
// Filter upper layer polygons in intersection_ppl by their bounding boxes?
// my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
double total_loop_length = 0;
for (Polygons::const_iterator it = upper_layerm_polygons.begin(); it != upper_layerm_polygons.end(); ++it)
total_loop_length += it->length();
const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing();
const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter);
const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width();
const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing();
for (Surfaces::iterator slice = layerm.slices.surfaces.begin();
slice != layerm.slices.surfaces.end(); ++slice) {
while (true) {
// compute the total thickness of perimeters
const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
+ (region.config.perimeters-1 + region.config.extra_perimeters) * perimeter_spacing;
// define a critical area where we don't want the upper slice to fall into
// (it should either lay over our perimeters or outside this area)
const coord_t critical_area_depth = perimeter_spacing * 1.5;
const Polygons critical_area = diff(
offset(slice->expolygon, -perimeters_thickness),
offset(slice->expolygon, -(perimeters_thickness + critical_area_depth))
);
// check whether a portion of the upper slices falls inside the critical area
const Polylines intersection = intersection_pl(
to_polylines(upper_layerm_polygons),
critical_area
);
// only add an additional loop if at least 30% of the slice loop would benefit from it
{
double total_intersection_length = 0;
for (Polylines::const_iterator it = intersection.begin(); it != intersection.end(); ++it)
total_intersection_length += it->length();
if (total_intersection_length <= total_loop_length*0.3) break;
}
/*
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"extra.svg",
no_arrows => 1,
expolygons => union_ex($critical_area),
polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
);
}
*/
slice->extra_perimeters++;
}
#ifdef DEBUG
if (slice->extra_perimeters > 0)
printf(" adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, i);
#endif
}
}
}
parallelize<Layer*>(
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
boost::bind(&Slic3r::Layer::make_perimeters, _1),
this->_print->config.threads.value
);
/*
simplify slices (both layer and region slices),
we only need the max resolution for perimeters
### This makes this method not-idempotent, so we keep it disabled for now.
###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
*/
this->state.set_done(posPerimeters);
}
void
PrintObject::_infill()
{
if (this->state.is_done(posInfill)) return;
this->state.set_started(posInfill);
parallelize<Layer*>(
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
boost::bind(&Slic3r::Layer::make_fills, _1),
this->_print->config.threads.value
);
/* we could free memory now, but this would make this step not idempotent
### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
*/
this->state.set_done(posInfill);
}
void PrintObject::_generate_support_material()
{
PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters());
support_material.generate(*this);
}
} // namespace Slic3r

View File

@ -12,7 +12,7 @@ namespace Slic3r {
class SVG
{
public:
public:
bool arrows;
std::string fill, stroke;
Point origin;
@ -89,6 +89,10 @@ public:
static void export_expolygons(const char *path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0);
static void export_expolygons(const std::string &path, const BoundingBox &bbox, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0)
{ export_expolygons(path.c_str(), bbox, expolygons, stroke_outer, stroke_holes, stroke_width); }
static void export_expolygons(const char *path, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0)
{ export_expolygons(path, get_extents(expolygons), expolygons, stroke_outer, stroke_holes, stroke_width); }
static void export_expolygons(const std::string &path, const Slic3r::ExPolygons &expolygons, std::string stroke_outer = "black", std::string stroke_holes = "blue", coordf_t stroke_width = 0)
{ export_expolygons(path.c_str(), get_extents(expolygons), expolygons, stroke_outer, stroke_holes, stroke_width); }
};
}

View File

@ -0,0 +1,631 @@
#include "Slicing.hpp"
#include "SlicingAdaptive.hpp"
#include "PrintConfig.hpp"
#include "Model.hpp"
// #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
#ifdef SLIC3R_DEBUG
#undef NDEBUG
#define DEBUG
#define _DEBUG
#include "SVG.hpp"
#undef assert
#include <cassert>
#endif
namespace Slic3r
{
SlicingParameters SlicingParameters::create_from_config(
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
coordf_t object_height,
const std::set<size_t> &object_extruders)
{
coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ?
object_config.layer_height.value :
object_config.first_layer_height.get_abs_value(object_config.layer_height.value);
coordf_t support_material_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_extruder.value - 1);
coordf_t support_material_interface_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_interface_extruder.value - 1);
bool soluble_interface = object_config.support_material_contact_distance.value == 0.;
SlicingParameters params;
params.layer_height = object_config.layer_height.value;
params.first_print_layer_height = first_layer_height;
params.first_object_layer_height = first_layer_height;
params.object_print_z_min = 0.;
params.object_print_z_max = object_height;
params.base_raft_layers = object_config.raft_layers.value;
params.soluble_interface = soluble_interface;
if (! soluble_interface) {
params.gap_raft_object = object_config.support_material_contact_distance.value;
params.gap_object_support = object_config.support_material_contact_distance.value;
params.gap_support_object = object_config.support_material_contact_distance.value;
}
if (params.base_raft_layers > 0) {
params.interface_raft_layers = (params.base_raft_layers + 1) / 2;
params.base_raft_layers -= params.interface_raft_layers;
// Use as large as possible layer height for the intermediate raft layers.
params.base_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_extruder_dmr);
params.interface_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr);
params.contact_raft_layer_height_bridging = false;
params.first_object_layer_bridging = false;
#if 1
params.contact_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr);
if (! soluble_interface) {
// Compute the average of all nozzles used for printing the object over a raft.
//FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa?
coordf_t average_object_extruder_dmr = 0.;
if (! object_extruders.empty()) {
for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder)
average_object_extruder_dmr += print_config.nozzle_diameter.get_at(*it_extruder);
average_object_extruder_dmr /= coordf_t(object_extruders.size());
}
params.first_object_layer_height = average_object_extruder_dmr;
params.first_object_layer_bridging = true;
}
#else
params.contact_raft_layer_height = soluble_interface ? support_material_interface_extruder_dmr : 0.75 * support_material_interface_extruder_dmr;
params.contact_raft_layer_height_bridging = ! soluble_interface;
...
#endif
}
if (params.has_raft()) {
// Raise first object layer Z by the thickness of the raft itself plus the extra distance required by the support material logic.
//FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case.
if (params.raft_layers() == 1) {
// There is only the contact layer.
params.contact_raft_layer_height = first_layer_height;
params.raft_contact_top_z = first_layer_height;
} else {
assert(params.base_raft_layers > 0);
assert(params.interface_raft_layers > 0);
// Number of the base raft layers is decreased by the first layer.
params.raft_base_top_z = first_layer_height + coordf_t(params.base_raft_layers - 1) * params.base_raft_layer_height;
// Number of the interface raft layers is decreased by the contact layer.
params.raft_interface_top_z = params.raft_base_top_z + coordf_t(params.interface_raft_layers - 1) * params.interface_raft_layer_height;
params.raft_contact_top_z = params.raft_interface_top_z + params.contact_raft_layer_height;
}
coordf_t print_z = params.raft_contact_top_z + params.gap_raft_object;
params.object_print_z_min = print_z;
params.object_print_z_max += print_z;
}
params.min_layer_height = std::min(params.layer_height, first_layer_height);
params.max_layer_height = std::max(params.layer_height, first_layer_height);
//FIXME add it to the print configuration
params.min_layer_height = 0.05;
// Calculate the maximum layer height as 0.75 from the minimum nozzle diameter.
if (! object_extruders.empty()) {
coordf_t min_object_extruder_dmr = 1000000.;
for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder)
min_object_extruder_dmr = std::min(min_object_extruder_dmr, print_config.nozzle_diameter.get_at(*it_extruder));
// Allow excessive maximum layer height higher than 0.75 * min_object_extruder_dmr
params.max_layer_height = std::max(std::max(params.layer_height, first_layer_height), 0.75 * min_object_extruder_dmr);
}
return params;
}
// Convert layer_height_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
// in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation.
std::vector<coordf_t> layer_height_profile_from_ranges(
const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges)
{
// 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed.
std::vector<std::pair<t_layer_height_range,coordf_t>> ranges_non_overlapping;
ranges_non_overlapping.reserve(layer_height_ranges.size() * 4);
if (slicing_params.first_object_layer_height_fixed())
ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(
t_layer_height_range(0., slicing_params.first_object_layer_height),
slicing_params.first_object_layer_height));
// The height ranges are sorted lexicographically by low / high layer boundaries.
for (t_layer_height_ranges::const_iterator it_range = layer_height_ranges.begin(); it_range != layer_height_ranges.end(); ++ it_range) {
coordf_t lo = it_range->first.first;
coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height());
coordf_t height = it_range->second;
if (! ranges_non_overlapping.empty())
// Trim current low with the last high.
lo = std::max(lo, ranges_non_overlapping.back().first.second);
if (lo + EPSILON < hi)
// Ignore too narrow ranges.
ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(t_layer_height_range(lo, hi), height));
}
// 2) Convert the trimmed ranges to a height profile, fill in the undefined intervals between z=0 and z=slicing_params.object_print_z_max()
// with slicing_params.layer_height
std::vector<coordf_t> layer_height_profile;
for (std::vector<std::pair<t_layer_height_range,coordf_t>>::const_iterator it_range = ranges_non_overlapping.begin(); it_range != ranges_non_overlapping.end(); ++ it_range) {
coordf_t lo = it_range->first.first;
coordf_t hi = it_range->first.second;
coordf_t height = it_range->second;
coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
if (lo > last_z + EPSILON) {
// Insert a step of normal layer height.
layer_height_profile.push_back(last_z);
layer_height_profile.push_back(slicing_params.layer_height);
layer_height_profile.push_back(lo);
layer_height_profile.push_back(slicing_params.layer_height);
}
// Insert a step of the overriden layer height.
layer_height_profile.push_back(lo);
layer_height_profile.push_back(height);
layer_height_profile.push_back(hi);
layer_height_profile.push_back(height);
}
coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
if (last_z < slicing_params.object_print_z_height()) {
// Insert a step of normal layer height up to the object top.
layer_height_profile.push_back(last_z);
layer_height_profile.push_back(slicing_params.layer_height);
layer_height_profile.push_back(slicing_params.object_print_z_height());
layer_height_profile.push_back(slicing_params.layer_height);
}
return layer_height_profile;
}
// Based on the work of @platsch
// Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
std::vector<coordf_t> layer_height_profile_adaptive(
const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges,
const ModelVolumePtrs &volumes)
{
// 1) Initialize the SlicingAdaptive class with the object meshes.
SlicingAdaptive as;
as.set_slicing_parameters(slicing_params);
for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it)
if (! (*it)->modifier)
as.add_mesh(&(*it)->mesh);
as.prepare();
// 2) Generate layers using the algorithm of @platsch
// loop until we have at least one layer and the max slice_z reaches the object height
//FIXME make it configurable
// Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm.
const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value');
std::vector<coordf_t> layer_height_profile;
layer_height_profile.push_back(0.);
layer_height_profile.push_back(slicing_params.first_object_layer_height);
if (slicing_params.first_object_layer_height_fixed()) {
layer_height_profile.push_back(slicing_params.first_object_layer_height);
layer_height_profile.push_back(slicing_params.first_object_layer_height);
}
coordf_t slice_z = slicing_params.first_object_layer_height;
coordf_t height = slicing_params.first_object_layer_height;
coordf_t cusp_height = 0.;
int current_facet = 0;
while ((slice_z - height) <= slicing_params.object_print_z_height()) {
height = 999;
// Slic3r::debugf "\n Slice layer: %d\n", $id;
// determine next layer height
coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet);
// check for horizontal features and object size
/*
if($self->config->get_value('match_horizontal_surfaces')) {
my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height);
if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) {
Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist;
# can we shrink the current layer a bit?
if($cusp_height-($min_height-$horizontal_dist) > $min_height) {
# yes we can
$cusp_height = $cusp_height-($min_height-$horizontal_dist);
Slic3r::debugf "Shrink layer height to %f\n", $cusp_height;
}else{
# no, current layer would become too thin
$cusp_height = $cusp_height+$horizontal_dist;
Slic3r::debugf "Widen layer height to %f\n", $cusp_height;
}
}
}
*/
height = std::min(cusp_height, height);
// apply z-gradation
/*
my $gradation = $self->config->get_value('adaptive_slicing_z_gradation');
if($gradation > 0) {
$height = $height - unscale((scale($height)) % (scale($gradation)));
}
*/
// look for an applicable custom range
/*
if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) {
$height = $range->[2];
# if user set custom height to zero we should just skip the range and resume slicing over it
if ($height == 0) {
$slice_z += $range->[1] - $range->[0];
next;
}
}
*/
layer_height_profile.push_back(slice_z);
layer_height_profile.push_back(height);
slice_z += height;
layer_height_profile.push_back(slice_z);
layer_height_profile.push_back(height);
}
coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]);
layer_height_profile.push_back(last);
layer_height_profile.push_back(slicing_params.first_object_layer_height);
layer_height_profile.push_back(slicing_params.object_print_z_height());
layer_height_profile.push_back(slicing_params.first_object_layer_height);
return layer_height_profile;
}
template <typename T>
static inline T clamp(const T low, const T high, const T value)
{
return std::max(low, std::min(high, value));
}
template <typename T>
static inline T lerp(const T a, const T b, const T t)
{
assert(t >= T(-EPSILON) && t <= T(1.+EPSILON));
return (1. - t) * a + t * b;
}
void adjust_layer_height_profile(
const SlicingParameters &slicing_params,
std::vector<coordf_t> &layer_height_profile,
coordf_t z,
coordf_t layer_thickness_delta,
coordf_t band_width,
LayerHeightEditActionType action)
{
// Constrain the profile variability by the 1st layer height.
std::pair<coordf_t, coordf_t> z_span_variable =
std::pair<coordf_t, coordf_t>(
slicing_params.first_object_layer_height_fixed() ? slicing_params.first_object_layer_height : 0.,
slicing_params.object_print_z_height());
if (z < z_span_variable.first || z > z_span_variable.second)
return;
assert(layer_height_profile.size() >= 2);
// 1) Get the current layer thickness at z.
coordf_t current_layer_height = slicing_params.layer_height;
for (size_t i = 0; i < layer_height_profile.size(); i += 2) {
if (i + 2 == layer_height_profile.size()) {
current_layer_height = layer_height_profile[i + 1];
break;
} else if (layer_height_profile[i + 2] > z) {
coordf_t z1 = layer_height_profile[i];
coordf_t h1 = layer_height_profile[i + 1];
coordf_t z2 = layer_height_profile[i + 2];
coordf_t h2 = layer_height_profile[i + 3];
current_layer_height = lerp(h1, h2, (z - z1) / (z2 - z1));
break;
}
}
// 2) Is it possible to apply the delta?
switch (action) {
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:
layer_thickness_delta = - layer_thickness_delta;
// fallthrough
case LAYER_HEIGHT_EDIT_ACTION_INCREASE:
if (layer_thickness_delta > 0) {
if (current_layer_height >= slicing_params.max_layer_height - EPSILON)
return;
layer_thickness_delta = std::min(layer_thickness_delta, slicing_params.max_layer_height - current_layer_height);
} else {
if (current_layer_height <= slicing_params.min_layer_height + EPSILON)
return;
layer_thickness_delta = std::max(layer_thickness_delta, slicing_params.min_layer_height - current_layer_height);
}
break;
case LAYER_HEIGHT_EDIT_ACTION_REDUCE:
case LAYER_HEIGHT_EDIT_ACTION_SMOOTH:
layer_thickness_delta = std::abs(layer_thickness_delta);
layer_thickness_delta = std::min(layer_thickness_delta, std::abs(slicing_params.layer_height - current_layer_height));
if (layer_thickness_delta < EPSILON)
return;
break;
default:
assert(false);
break;
}
// 3) Densify the profile inside z +- band_width/2, remove duplicate Zs from the height profile inside the band.
coordf_t lo = std::max(z_span_variable.first, z - 0.5 * band_width);
coordf_t hi = std::min(z_span_variable.second, z + 0.5 * band_width);
coordf_t z_step = 0.1;
size_t i = 0;
while (i < layer_height_profile.size() && layer_height_profile[i] < lo)
i += 2;
i -= 2;
std::vector<double> profile_new;
profile_new.reserve(layer_height_profile.size());
assert(i >= 0 && i + 1 < layer_height_profile.size());
profile_new.insert(profile_new.end(), layer_height_profile.begin(), layer_height_profile.begin() + i + 2);
coordf_t zz = lo;
size_t i_resampled_start = profile_new.size();
while (zz < hi) {
size_t next = i + 2;
coordf_t z1 = layer_height_profile[i];
coordf_t h1 = layer_height_profile[i + 1];
coordf_t height = h1;
if (next < layer_height_profile.size()) {
coordf_t z2 = layer_height_profile[next];
coordf_t h2 = layer_height_profile[next + 1];
height = lerp(h1, h2, (zz - z1) / (z2 - z1));
}
// Adjust height by layer_thickness_delta.
coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.;
coordf_t height_new = height;
switch (action) {
case LAYER_HEIGHT_EDIT_ACTION_INCREASE:
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:
height += weight * layer_thickness_delta;
break;
case LAYER_HEIGHT_EDIT_ACTION_REDUCE:
{
coordf_t delta = height - slicing_params.layer_height;
coordf_t step = weight * layer_thickness_delta;
step = (std::abs(delta) > step) ?
(delta > 0) ? -step : step :
-delta;
height += step;
break;
}
case LAYER_HEIGHT_EDIT_ACTION_SMOOTH:
{
// Don't modify the profile during resampling process, do it at the next step.
break;
}
default:
assert(false);
break;
}
// Avoid entering a too short segment.
if (profile_new[profile_new.size() - 2] + EPSILON < zz) {
profile_new.push_back(zz);
profile_new.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, height));
}
zz += z_step;
i = next;
while (i < layer_height_profile.size() && layer_height_profile[i] < zz)
i += 2;
i -= 2;
}
i += 2;
assert(i > 0);
size_t i_resampled_end = profile_new.size();
if (i < layer_height_profile.size()) {
assert(zz >= layer_height_profile[i - 2]);
assert(zz <= layer_height_profile[i]);
// profile_new.push_back(zz);
// profile_new.push_back(layer_height_profile[i + 1]);
profile_new.insert(profile_new.end(), layer_height_profile.begin() + i, layer_height_profile.end());
}
layer_height_profile = std::move(profile_new);
if (action == LAYER_HEIGHT_EDIT_ACTION_SMOOTH) {
size_t n_rounds = 6;
for (size_t i_round = 0; i_round < n_rounds; ++ i_round) {
profile_new = layer_height_profile;
for (size_t i = i_resampled_start; i < i_resampled_end; i += 2) {
coordf_t zz = profile_new[i];
coordf_t t = std::abs(zz - z) < 0.5 * band_width ? (0.25 + 0.25 * cos(2. * M_PI * (zz - z) / band_width)) : 0.;
assert(t >= 0. && t <= 0.5000001);
if (i == 0)
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i + 3];
else if (i + 1 == profile_new.size())
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i - 1];
else
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + 0.5 * t * (profile_new[i - 1] + profile_new[i + 3]);
}
}
}
assert(layer_height_profile.size() > 2);
assert(layer_height_profile.size() % 2 == 0);
assert(layer_height_profile[0] == 0.);
#ifdef _DEBUG
for (size_t i = 2; i < layer_height_profile.size(); i += 2)
assert(layer_height_profile[i - 2] <= layer_height_profile[i]);
for (size_t i = 1; i < layer_height_profile.size(); i += 2) {
assert(layer_height_profile[i] > slicing_params.min_layer_height - EPSILON);
assert(layer_height_profile[i] < slicing_params.max_layer_height + EPSILON);
}
#endif /* _DEBUG */
}
// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector.
std::vector<coordf_t> generate_object_layers(
const SlicingParameters &slicing_params,
const std::vector<coordf_t> &layer_height_profile)
{
coordf_t print_z = 0;
coordf_t height = 0;
std::vector<coordf_t> out;
if (slicing_params.first_object_layer_height_fixed()) {
out.push_back(0);
print_z = slicing_params.first_object_layer_height;
out.push_back(print_z);
}
size_t idx_layer_height_profile = 0;
// loop until we have at least one layer and the max slice_z reaches the object height
coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height;
while (slice_z < slicing_params.object_print_z_height()) {
height = slicing_params.min_layer_height;
if (idx_layer_height_profile < layer_height_profile.size()) {
size_t next = idx_layer_height_profile + 2;
for (;;) {
if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next])
break;
idx_layer_height_profile = next;
next += 2;
}
coordf_t z1 = layer_height_profile[idx_layer_height_profile];
coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1];
height = h1;
if (next < layer_height_profile.size()) {
coordf_t z2 = layer_height_profile[next];
coordf_t h2 = layer_height_profile[next + 1];
height = lerp(h1, h2, (slice_z - z1) / (z2 - z1));
assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON);
}
}
slice_z = print_z + 0.5 * height;
if (slice_z >= slicing_params.object_print_z_height())
break;
assert(height > slicing_params.min_layer_height - EPSILON);
assert(height < slicing_params.max_layer_height + EPSILON);
out.push_back(print_z);
print_z += height;
slice_z = print_z + 0.5 * slicing_params.min_layer_height;
out.push_back(print_z);
}
//FIXME Adjust the last layer to align with the top object layer exactly?
return out;
}
int generate_layer_height_texture(
const SlicingParameters &slicing_params,
const std::vector<coordf_t> &layers,
void *data, int rows, int cols, bool level_of_detail_2nd_level)
{
// https://github.com/aschn/gnuplot-colorbrewer
std::vector<Point3> palette_raw;
palette_raw.push_back(Point3(0x0B2, 0x018, 0x02B));
palette_raw.push_back(Point3(0x0D6, 0x060, 0x04D));
palette_raw.push_back(Point3(0x0F4, 0x0A5, 0x082));
palette_raw.push_back(Point3(0x0FD, 0x0DB, 0x0C7));
palette_raw.push_back(Point3(0x0D1, 0x0E5, 0x0F0));
palette_raw.push_back(Point3(0x092, 0x0C5, 0x0DE));
palette_raw.push_back(Point3(0x043, 0x093, 0x0C3));
palette_raw.push_back(Point3(0x021, 0x066, 0x0AC));
// Clear the main texture and the 2nd LOD level.
memset(data, 0, rows * cols * 5);
// 2nd LOD level data start
unsigned char *data1 = reinterpret_cast<unsigned char*>(data) + rows * cols * 4;
int ncells = std::min((cols-1) * rows, int(ceil(16. * (slicing_params.object_print_z_height() / slicing_params.min_layer_height))));
int ncells1 = ncells / 2;
int cols1 = cols / 2;
coordf_t z_to_cell = coordf_t(ncells-1) / slicing_params.object_print_z_height();
coordf_t cell_to_z = slicing_params.object_print_z_height() / coordf_t(ncells-1);
coordf_t z_to_cell1 = coordf_t(ncells1-1) / slicing_params.object_print_z_height();
coordf_t cell_to_z1 = slicing_params.object_print_z_height() / coordf_t(ncells1-1);
// for color scaling
coordf_t hscale = 2.f * std::max(slicing_params.max_layer_height - slicing_params.layer_height, slicing_params.layer_height - slicing_params.min_layer_height);
if (hscale == 0)
// All layers have the same height. Provide some height scale to avoid division by zero.
hscale = slicing_params.layer_height;
for (size_t idx_layer = 0; idx_layer < layers.size(); idx_layer += 2) {
coordf_t lo = layers[idx_layer];
coordf_t hi = layers[idx_layer + 1];
coordf_t mid = 0.5f * (lo + hi);
assert(mid <= slicing_params.object_print_z_height());
coordf_t h = hi - lo;
hi = std::min(hi, slicing_params.object_print_z_height());
int cell_first = clamp(0, ncells-1, int(ceil(lo * z_to_cell)));
int cell_last = clamp(0, ncells-1, int(floor(hi * z_to_cell)));
for (int cell = cell_first; cell <= cell_last; ++ cell) {
coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale;
int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf)));
int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1);
coordf_t t = idxf - coordf_t(idx1);
const Point3 &color1 = palette_raw[idx1];
const Point3 &color2 = palette_raw[idx2];
coordf_t z = cell_to_z * coordf_t(cell);
assert(z >= lo && z <= hi);
// Intensity profile to visualize the layers.
coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h);
// Color mapping from layer height to RGB.
Pointf3 color(
intensity * lerp(coordf_t(color1.x), coordf_t(color2.x), t),
intensity * lerp(coordf_t(color1.y), coordf_t(color2.y), t),
intensity * lerp(coordf_t(color1.z), coordf_t(color2.z), t));
int row = cell / (cols - 1);
int col = cell - row * (cols - 1);
assert(row >= 0 && row < rows);
assert(col >= 0 && col < cols);
unsigned char *ptr = (unsigned char*)data + (row * cols + col) * 4;
ptr[0] = clamp<int>(0, 255, int(floor(color.x + 0.5)));
ptr[1] = clamp<int>(0, 255, int(floor(color.y + 0.5)));
ptr[2] = clamp<int>(0, 255, int(floor(color.z + 0.5)));
ptr[3] = 255;
if (col == 0 && row > 0) {
// Duplicate the first value in a row as a last value of the preceding row.
ptr[-4] = ptr[0];
ptr[-3] = ptr[1];
ptr[-2] = ptr[2];
ptr[-1] = ptr[3];
}
}
if (level_of_detail_2nd_level) {
cell_first = clamp(0, ncells1-1, int(ceil(lo * z_to_cell1)));
cell_last = clamp(0, ncells1-1, int(floor(hi * z_to_cell1)));
for (int cell = cell_first; cell <= cell_last; ++ cell) {
coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale;
int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf)));
int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1);
coordf_t t = idxf - coordf_t(idx1);
const Point3 &color1 = palette_raw[idx1];
const Point3 &color2 = palette_raw[idx2];
coordf_t z = cell_to_z1 * coordf_t(cell);
assert(z >= lo && z <= hi);
// Color mapping from layer height to RGB.
Pointf3 color(
lerp(coordf_t(color1.x), coordf_t(color2.x), t),
lerp(coordf_t(color1.y), coordf_t(color2.y), t),
lerp(coordf_t(color1.z), coordf_t(color2.z), t));
int row = cell / (cols1 - 1);
int col = cell - row * (cols1 - 1);
assert(row >= 0 && row < rows/2);
assert(col >= 0 && col < cols/2);
unsigned char *ptr = data1 + (row * cols1 + col) * 4;
ptr[0] = clamp<int>(0, 255, int(floor(color.x + 0.5)));
ptr[1] = clamp<int>(0, 255, int(floor(color.y + 0.5)));
ptr[2] = clamp<int>(0, 255, int(floor(color.z + 0.5)));
ptr[3] = 255;
if (col == 0 && row > 0) {
// Duplicate the first value in a row as a last value of the preceding row.
ptr[-4] = ptr[0];
ptr[-3] = ptr[1];
ptr[-2] = ptr[2];
ptr[-1] = ptr[3];
}
}
}
}
// Returns number of cells of the 0th LOD level.
return ncells;
}
}; // namespace Slic3r

View File

@ -0,0 +1,138 @@
// Based on implementation by @platsch
#ifndef slic3r_Slicing_hpp_
#define slic3r_Slicing_hpp_
#include <set>
#include <vector>
#include "libslic3r.h"
namespace Slic3r
{
class PrintConfig;
class PrintObjectConfig;
class ModelVolume;
typedef std::vector<ModelVolume*> ModelVolumePtrs;
// Parameters to guide object slicing and support generation.
// The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow
// (using a normal flow over a soluble support, using a bridging flow over a non-soluble support).
struct SlicingParameters
{
SlicingParameters() { memset(this, 0, sizeof(SlicingParameters)); }
static SlicingParameters create_from_config(
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
coordf_t object_height,
const std::set<size_t> &object_extruders);
// Has any raft layers?
bool has_raft() const { return raft_layers() > 0; }
size_t raft_layers() const { return base_raft_layers + interface_raft_layers; }
// Is the 1st object layer height fixed, or could it be varied?
bool first_object_layer_height_fixed() const { return ! has_raft() || first_object_layer_bridging; }
// Height of the object to be printed. This value does not contain the raft height.
coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; }
// Number of raft layers.
size_t base_raft_layers;
// Number of interface layers including the contact layer.
size_t interface_raft_layers;
// Layer heights of the raft (base, interface and a contact layer).
coordf_t base_raft_layer_height;
coordf_t interface_raft_layer_height;
coordf_t contact_raft_layer_height;
bool contact_raft_layer_height_bridging;
// The regular layer height, applied for all but the first layer, if not overridden by layer ranges
// or by the variable layer thickness table.
coordf_t layer_height;
// First layer height of the print, this may be used for the first layer of the raft
// or for the first layer of the print.
coordf_t first_print_layer_height;
// Thickness of the first layer. This is either the first print layer thickness if printed without a raft,
// or a bridging flow thickness if printed over a non-soluble raft,
// or a normal layer height if printed over a soluble raft.
coordf_t first_object_layer_height;
// If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow.
bool first_object_layer_bridging;
// Soluble interface? (PLA soluble in water, HIPS soluble in lemonen)
// otherwise the interface must be broken off.
bool soluble_interface;
// Gap when placing object over raft.
coordf_t gap_raft_object;
// Gap when placing support over object.
coordf_t gap_object_support;
// Gap when placing object over support.
coordf_t gap_support_object;
// Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm,
// or by an interactive layer height editor.
coordf_t min_layer_height;
coordf_t max_layer_height;
// Bottom and top of the printed object.
// If printed without a raft, object_print_z_min = 0 and object_print_z_max = object height.
// Otherwise object_print_z_min is equal to the raft height.
coordf_t raft_base_top_z;
coordf_t raft_interface_top_z;
coordf_t raft_contact_top_z;
// In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer.
coordf_t object_print_z_min;
coordf_t object_print_z_max;
};
typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
extern std::vector<coordf_t> layer_height_profile_from_ranges(
const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges);
extern std::vector<coordf_t> layer_height_profile_adaptive(
const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges,
const ModelVolumePtrs &volumes);
enum LayerHeightEditActionType {
LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0,
LAYER_HEIGHT_EDIT_ACTION_DECREASE = 1,
LAYER_HEIGHT_EDIT_ACTION_REDUCE = 2,
LAYER_HEIGHT_EDIT_ACTION_SMOOTH = 3
};
extern void adjust_layer_height_profile(
const SlicingParameters &slicing_params,
std::vector<coordf_t> &layer_height_profile,
coordf_t z,
coordf_t layer_thickness_delta,
coordf_t band_width,
LayerHeightEditActionType action);
// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector.
// The object layers are based at z=0, ignoring the raft layers.
extern std::vector<coordf_t> generate_object_layers(
const SlicingParameters &slicing_params,
const std::vector<coordf_t> &layer_height_profile);
// Produce a 1D texture packed into a 2D texture describing in the RGBA format
// the planned object layers.
// Returns number of cells used by the texture of the 0th LOD level.
extern int generate_layer_height_texture(
const SlicingParameters &slicing_params,
const std::vector<coordf_t> &layers,
void *data, int rows, int cols, bool level_of_detail_2nd_level);
}; // namespace Slic3r
#endif /* slic3r_Slicing_hpp_ */

View File

@ -0,0 +1,140 @@
#include "libslic3r.h"
#include "TriangleMesh.hpp"
#include "SlicingAdaptive.hpp"
namespace Slic3r
{
void SlicingAdaptive::clear()
{
m_meshes.clear();
m_faces.clear();
m_face_normal_z.clear();
}
std::pair<float, float> face_z_span(const stl_facet *f)
{
return std::pair<float, float>(
std::min(std::min(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z),
std::max(std::max(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z));
}
void SlicingAdaptive::prepare()
{
// 1) Collect faces of all meshes.
int nfaces_total = 0;
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
nfaces_total += (*it_mesh)->stl.stats.number_of_facets;
m_faces.reserve(nfaces_total);
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i)
m_faces.push_back((*it_mesh)->stl.facet_start + i);
// 2) Sort faces lexicographically by their Z span.
std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) {
std::pair<float, float> span1 = face_z_span(f1);
std::pair<float, float> span2 = face_z_span(f2);
return span1 < span2;
});
// 3) Generate Z components of the facet normals.
m_face_normal_z.assign(m_faces.size(), 0.f);
for (size_t iface = 0; iface < m_faces.size(); ++ iface)
m_face_normal_z[iface] = m_faces[iface]->normal.z;
}
float SlicingAdaptive::cusp_height(float z, float cusp_value, int &current_facet)
{
float height = m_slicing_params.max_layer_height;
bool first_hit = false;
// find all facets intersecting the slice-layer
int ordered_id = current_facet;
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
// facet's minimum is higher than slice_z -> end loop
if (zspan.first >= z)
break;
// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
if (zspan.second > z) {
// first event?
if (! first_hit) {
first_hit = true;
current_facet = ordered_id;
}
// skip touching facets which could otherwise cause small cusp values
if (zspan.second <= z + EPSILON)
continue;
// compute cusp-height for this facet and store minimum of all heights
float normal_z = m_face_normal_z[ordered_id];
height = std::min(height, (normal_z == 0.f) ? 9999.f : std::abs(cusp_value / normal_z));
}
}
// lower height limit due to printer capabilities
height = std::max(height, float(m_slicing_params.min_layer_height));
// check for sloped facets inside the determined layer and correct height if necessary
if (height > m_slicing_params.min_layer_height) {
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
// facet's minimum is higher than slice_z + height -> end loop
if (zspan.first >= z + height)
break;
// skip touching facets which could otherwise cause small cusp values
if (zspan.second <= z + EPSILON)
continue;
// Compute cusp-height for this facet and check against height.
float normal_z = m_face_normal_z[ordered_id];
float cusp = (normal_z == 0) ? 9999 : abs(cusp_value / normal_z);
float z_diff = zspan.first - z;
// handle horizontal facets
if (m_face_normal_z[ordered_id] > 0.999) {
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
height = z_diff;
// Slic3r::debugf "to %f due to near horizontal facet\n", $height;
} else if (cusp > z_diff) {
if (cusp < height) {
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
height = cusp;
// Slic3r::debugf "to %f due to new cusp height\n", $height;
}
} else {
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
height = z_diff;
// Slic3r::debugf "to z-diff: %f\n", $height;
}
}
// lower height limit due to printer capabilities again
height = std::max(height, float(m_slicing_params.min_layer_height));
}
// Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height;
return height;
}
// Returns the distance to the next horizontal facet in Z-dir
// to consider horizontal object features in slice thickness
float SlicingAdaptive::horizontal_facet_distance(float z)
{
for (size_t i = 0; i < m_faces.size(); ++ i) {
std::pair<float, float> zspan = face_z_span(m_faces[i]);
// facet's minimum is higher than max forward distance -> end loop
if (zspan.first > z + m_slicing_params.max_layer_height)
break;
// min_z == max_z -> horizontal facet
if (zspan.first > z && zspan.first == zspan.second)
return zspan.first - z;
}
// objects maximum?
return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ?
std::max<float>(m_slicing_params.object_print_z_height() - z, 0.f) :
m_slicing_params.max_layer_height;
}
}; // namespace Slic3r

View File

@ -0,0 +1,36 @@
// Based on implementation by @platsch
#ifndef slic3r_SlicingAdaptive_hpp_
#define slic3r_SlicingAdaptive_hpp_
#include "Slicing.hpp"
#include "admesh/stl.h"
namespace Slic3r
{
class TriangleMesh;
class SlicingAdaptive
{
public:
void clear();
void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; }
void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); }
void prepare();
float cusp_height(float z, float cusp_value, int &current_facet);
float horizontal_facet_distance(float z);
protected:
SlicingParameters m_slicing_params;
std::vector<const TriangleMesh*> m_meshes;
// Collected faces of all meshes, sorted by raising Z of the bottom most face.
std::vector<const stl_facet*> m_faces;
// Z component of face normals, normalized.
std::vector<float> m_face_normal_z;
};
}; // namespace Slic3r
#endif /* slic3r_SlicingAdaptive_hpp_ */

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@
#include "Flow.hpp"
#include "PrintConfig.hpp"
#include "Slicing.hpp"
namespace Slic3r {
@ -20,19 +21,33 @@ class PrintObjectConfig;
class PrintObjectSupportMaterial
{
public:
// Support layer type to be used by MyLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
enum SupporLayerType {
sltUnknown = 0,
sltRaft,
stlFirstLayer,
// Ratft base layer, to be printed with the support material.
sltRaftBase,
// Raft interface layer, to be printed with the support interface material.
sltRaftInterface,
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
sltBottomContact,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object by an sltBottomContact layer.
sltBottomInterface,
// Sparse base support layer, to be printed with a support material.
sltBase,
// Dense interface layer, to be printed with the support interface material.
// This layer is separated from an object with sltTopContact layer.
sltTopInterface,
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
sltTopContact,
// Some undecided type yet. It will turn into stlBase first, then it may turn into stlBottomInterface or stlTopInterface.
stlIntermediate,
// Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface.
sltIntermediate,
};
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
// information about the support layer than the layers stored in the PrintObject, mainly
// the MyLayer is aware of the bridging flow and the interface gaps between the object and the support.
class MyLayer
{
public:
@ -57,6 +72,7 @@ public:
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
bool operator<(const MyLayer &layer2) const {
if (print_z < layer2.print_z) {
return true;
@ -72,12 +88,12 @@ public:
}
SupporLayerType layer_type;
// Z used for printing in unscaled coordinates
// Z used for printing, in unscaled coordinates.
coordf_t print_z;
// Bottom height of this layer. For soluble layers, bottom_z + height = print_z,
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
// otherwise bottom_z + gap + height = print_z.
coordf_t bottom_z;
// layer height in unscaled coordinates
// Layer height in unscaled coordinates.
coordf_t height;
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
// If this is not a contact layer, it will be set to size_t(-1).
@ -91,63 +107,31 @@ public:
// Polygons to be filled by the support pattern.
Polygons polygons;
// Currently for the contact layers only: Overhangs are stored here.
// MyLayer owns the aux_polygons, they are freed by the destructor.
Polygons *aux_polygons;
};
struct LayerExtreme
{
LayerExtreme(MyLayer *alayer, bool ais_top) : layer(alayer), is_top(ais_top) {}
MyLayer *layer;
// top or bottom extreme
bool is_top;
coordf_t z() const { return is_top ? layer->print_z : layer->print_z - layer->height; }
bool operator<(const LayerExtreme &other) const { return z() < other.z(); }
};
/*
struct LayerPrintZ_Hash {
size_t operator()(const MyLayer &layer) const {
return std::hash<double>()(layer.print_z)^std::hash<double>()(layer.height)^size_t(layer.bridging);
}
};
*/
typedef std::vector<MyLayer*> MyLayersPtr;
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
// which would allocate layers by multiple chunks.
typedef std::deque<MyLayer> MyLayerStorage;
typedef std::vector<MyLayer*> MyLayersPtr;
public:
PrintObjectSupportMaterial(const PrintObject *object);
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
// Height of the 1st layer is user configured as it is important for the print
// to stick to he print bed.
coordf_t first_layer_height() const { return m_object_config->first_layer_height.value; }
// Is raft enabled?
bool has_raft() const { return m_has_raft; }
bool has_raft() const { return m_slicing_params.has_raft(); }
// Has any support?
bool has_support() const { return m_object_config->support_material.value; }
bool build_plate_only() const { return this->has_support() && m_object_config->support_material_buildplate_only.value; }
// How many raft layers are there below the 1st object layer?
// The 1st object layer_id will be offsetted by this number.
size_t num_raft_layers() const { return m_object_config->raft_layers.value; }
// num_raft_layers() == num_raft_base_layers() + num_raft_interface_layers() + num_raft_contact_layers().
size_t num_raft_base_layers() const { return m_num_base_raft_layers; }
size_t num_raft_interface_layers() const { return m_num_interface_raft_layers; }
size_t num_raft_contact_layers() const { return m_num_contact_raft_layers; }
coordf_t raft_height() const { return m_raft_height; }
coordf_t raft_base_height() const { return m_raft_base_height; }
coordf_t raft_interface_height() const { return m_raft_interface_height; }
coordf_t raft_contact_height() const { return m_raft_contact_height; }
bool raft_bridging() const { return m_raft_contact_layer_bridging; }
// 1st layer of the object will be printed depeding on the raft settings.
coordf_t first_object_layer_print_z() const { return m_object_1st_layer_print_z; }
coordf_t first_object_layer_height() const { return m_object_1st_layer_height; }
coordf_t first_object_layer_gap() const { return m_object_1st_layer_gap; }
bool first_object_layer_bridging() const { return m_object_1st_layer_bridging; }
bool synchronize_layers() const { return m_object_config->support_material_synchronize_layers.value; }
bool has_contact_loops() const { return m_object_config->support_material_interface_contact_loops.value; }
// Generate support material for the object.
// New support layers will be added to the object,
@ -163,7 +147,9 @@ private:
// Generate bottom contact layers supporting the top contact layers.
// For a soluble interface material synchronize the layer heights with the object,
// otherwise set the layer height to a bridging flow of a support interface nozzle.
MyLayersPtr bottom_contact_layers(const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const;
MyLayersPtr bottom_contact_layers_and_layer_support_areas(
const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage,
std::vector<Polygons> &layer_support_areas) const;
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const;
@ -176,17 +162,23 @@ private:
MyLayerStorage &layer_storage,
const coordf_t max_object_layer_height) const;
// Fill in the base layers with polygons.
void generate_base_layers(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers) const;
MyLayersPtr &intermediate_layers,
std::vector<Polygons> &layer_support_areas) const;
Polygons generate_raft_base(
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
MyLayersPtr generate_raft_base(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
MyLayersPtr &intermediate_layers) const;
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
MyLayerStorage &layer_storage) const;
// Turn some of the base layers into interface layers.
MyLayersPtr generate_interface_layers(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
@ -194,6 +186,15 @@ private:
MyLayersPtr &intermediate_layers,
MyLayerStorage &layer_storage) const;
// Trim support layers by an object to leave a defined gap between
// the support volume and the object.
void trim_support_layers_by_object(
const PrintObject &object,
MyLayersPtr &support_layers,
const coordf_t gap_extra_above,
const coordf_t gap_extra_below,
const coordf_t gap_xy) const;
/*
void generate_pillars_shape();
void clip_with_shape();
@ -202,59 +203,28 @@ private:
// Produce the actual G-code.
void generate_toolpaths(
const PrintObject &object,
const Polygons &raft,
const MyLayersPtr &raft_layers,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
const MyLayersPtr &intermediate_layers,
const MyLayersPtr &interface_layers) const;
// Following objects are not owned by SupportMaterial class.
const PrintObject *m_object;
const PrintConfig *m_print_config;
const PrintObjectConfig *m_object_config;
// Pre-calculated parameters shared between the object slicer and the support generator,
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params;
Flow m_first_layer_flow;
Flow m_support_material_flow;
Flow m_support_material_interface_flow;
bool m_soluble_interface;
Flow m_support_material_raft_base_flow;
Flow m_support_material_raft_interface_flow;
Flow m_support_material_raft_contact_flow;
bool m_has_raft;
size_t m_num_base_raft_layers;
size_t m_num_interface_raft_layers;
size_t m_num_contact_raft_layers;
// If set, the raft contact layer is laid with round strings, which are easily detachable
// from both the below and above layes.
// Otherwise a normal flow is used and the strings are squashed against the layer below,
// creating a firm bond with the layer below and making the interface top surface flat.
coordf_t m_raft_height;
coordf_t m_raft_base_height;
coordf_t m_raft_interface_height;
coordf_t m_raft_contact_height;
bool m_raft_contact_layer_bridging;
coordf_t m_object_1st_layer_print_z;
coordf_t m_object_1st_layer_height;
coordf_t m_object_1st_layer_gap;
bool m_object_1st_layer_bridging;
coordf_t m_object_layer_height_max;
coordf_t m_support_layer_height_min;
coordf_t m_support_layer_height_max;
coordf_t m_support_interface_layer_height_max;
coordf_t m_gap_extra_above;
coordf_t m_gap_extra_below;
coordf_t m_gap_xy;
// If enabled, the support layers will be synchronized with object layers.
// This does not prevent the support layers to be combined.
bool m_synchronize_support_layers_with_object;
// If disabled and m_synchronize_support_layers_with_object,
// the support layers will be synchronized with the object layers exactly, no layer will be combined.
bool m_combine_support_layers;
coordf_t m_gap_xy;
};
} // namespace Slic3r

View File

@ -57,6 +57,7 @@ public:
operator Polygons() const;
double area() const;
bool empty() const { return expolygon.empty(); }
void clear() { expolygon.clear(); }
bool is_solid() const;
bool is_external() const;
bool is_internal() const;

View File

@ -8,22 +8,12 @@ namespace Slic3r {
SurfaceCollection::operator Polygons() const
{
Polygons polygons;
for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
Polygons surface_p = surface->expolygon;
polygons.insert(polygons.end(), surface_p.begin(), surface_p.end());
}
return polygons;
return to_polygons(surfaces);
}
SurfaceCollection::operator ExPolygons() const
{
ExPolygons expp;
expp.reserve(this->surfaces.size());
for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
expp.push_back(surface->expolygon);
}
return expp;
return to_expolygons(surfaces);
}
void
@ -196,19 +186,6 @@ SurfaceCollection::remove_types(const SurfaceType *types, int ntypes)
surfaces.erase(surfaces.begin() + j, surfaces.end());
}
void
SurfaceCollection::append(const SurfaceCollection &coll)
{
this->surfaces.insert(this->surfaces.end(), coll.surfaces.begin(), coll.surfaces.end());
}
void
SurfaceCollection::append(const SurfaceType surfaceType, const Slic3r::ExPolygons &expoly)
{
for (Slic3r::ExPolygons::const_iterator it = expoly.begin(); it != expoly.end(); ++ it)
this->surfaces.push_back(Slic3r::Surface(surfaceType, *it));
}
void SurfaceCollection::export_to_svg(const char *path, bool show_labels)
{
BoundingBox bbox;

View File

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

View File

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

View File

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

View File

@ -0,0 +1,10 @@
#ifndef slic3r_Utils_hpp_
#define slic3r_Utils_hpp_
namespace Slic3r {
extern void set_logging_level(unsigned int level);
} // namespace Slic3r
#endif // slic3r_Utils_hpp_

View File

@ -4,10 +4,14 @@
// this needs to be included early for MSVC (listing it in Build.PL is not enough)
#include <ostream>
#include <iostream>
#include <math.h>
#include <queue>
#include <sstream>
#include <cstdio>
#include <stdint.h>
#include <stdarg.h>
#include <vector>
#include <boost/thread.hpp>
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
#define SLIC3R_VERSION "1.31.6"
@ -40,13 +44,6 @@
typedef long coord_t;
typedef double coordf_t;
namespace Slic3r {
enum Axis { X=0, Y, Z };
}
using namespace Slic3r;
/* Implementation of CONFESS("foo"): */
#ifdef _MSC_VER
#define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__)
@ -91,4 +88,55 @@ inline std::string debug_out_path(const char *name, ...)
// Write slices as SVG images into out directory during the 2D processing of the slices.
// #define SLIC3R_DEBUG_SLICE_PROCESSING
namespace Slic3r {
enum Axis { X=0, Y, Z };
template <class T>
inline void append_to(std::vector<T> &dst, const std::vector<T> &src)
{
dst.insert(dst.end(), src.begin(), src.end());
}
template <class T> void
_parallelize_do(std::queue<T>* queue, boost::mutex* queue_mutex, boost::function<void(T)> func)
{
//std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl;
while (true) {
T i;
{
boost::lock_guard<boost::mutex> l(*queue_mutex);
if (queue->empty()) return;
i = queue->front();
queue->pop();
}
//std::cout << " Thread " << boost::this_thread::get_id() << " processing item " << i << std::endl;
func(i);
boost::this_thread::interruption_point();
}
}
template <class T> void
parallelize(std::queue<T> queue, boost::function<void(T)> func,
int threads_count = boost::thread::hardware_concurrency())
{
if (threads_count == 0) threads_count = 2;
boost::mutex queue_mutex;
boost::thread_group workers;
for (int i = 0; i < std::min(threads_count, int(queue.size())); ++ i)
workers.add_thread(new boost::thread(&_parallelize_do<T>, &queue, &queue_mutex, func));
workers.join_all();
}
template <class T> void
parallelize(T start, T end, boost::function<void(T)> func,
int threads_count = boost::thread::hardware_concurrency())
{
std::queue<T> queue;
for (T i = start; i <= end; ++i) queue.push(i);
parallelize(queue, func, threads_count);
}
} // namespace Slic3r
#endif

View File

@ -1,3 +1,30 @@
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
namespace Slic3r {
static boost::log::trivial::severity_level logSeverity = boost::log::trivial::fatal;
void set_logging_level(unsigned int level)
{
switch (level) {
case 0: logSeverity = boost::log::trivial::fatal; break;
case 1: logSeverity = boost::log::trivial::error; break;
case 2: logSeverity = boost::log::trivial::warning; break;
case 3: logSeverity = boost::log::trivial::info; break;
case 4: logSeverity = boost::log::trivial::debug; break;
default: logSeverity = boost::log::trivial::trace; break;
}
boost::log::core::get()->set_filter
(
boost::log::trivial::severity >= logSeverity
);
}
} // namespace Slic3r
#ifdef SLIC3R_HAS_BROKEN_CROAK
// Some Strawberry Perl builds (mainly the latest 64bit builds) have a broken mechanism

View File

@ -532,8 +532,7 @@ SV*
polynode2perl(const ClipperLib::PolyNode& node)
{
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 {

View File

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

View File

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

View File

@ -30,6 +30,7 @@
long y_min() %code{% RETVAL = THIS->min.y; %};
long y_max() %code{% RETVAL = THIS->max.y; %};
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld;%ld,%ld", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %};
bool defined() %code{% RETVAL = THIS->defined; %};
%{
@ -69,6 +70,7 @@ new_from_points(CLASS, points)
void set_y_min(double val) %code{% THIS->min.y = val; %};
void set_y_max(double val) %code{% THIS->max.y = val; %};
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf;%lf,%lf", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %};
bool defined() %code{% RETVAL = THIS->defined; %};
%{
@ -106,4 +108,5 @@ new_from_points(CLASS, points)
double z_min() %code{% RETVAL = THIS->min.z; %};
double z_max() %code{% RETVAL = THIS->max.z; %};
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf;%lf,%lf,%lf", THIS->min.x, THIS->min.y, THIS->min.z, THIS->max.x, THIS->max.y, THIS->max.z); RETVAL = buf; %};
bool defined() %code{% RETVAL = THIS->defined; %};
};

View File

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

View File

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

View File

@ -3,6 +3,7 @@
%{
#include <xsinit.h>
#include "libslic3r/Print.hpp"
#include "libslic3r/Slicing.hpp"
#include "libslic3r/PlaceholderParser.hpp"
%}
@ -58,6 +59,8 @@ _constant()
Points copies();
t_layer_height_ranges layer_height_ranges()
%code%{ RETVAL = THIS->layer_height_ranges; %};
std::vector<double> layer_height_profile()
%code%{ RETVAL = THIS->layer_height_profile; %};
Ref<Point3> size()
%code%{ RETVAL = &THIS->size; %};
Clone<BoundingBox> bounding_box();
@ -82,6 +85,8 @@ _constant()
bool reload_model_instances();
void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges)
%code%{ THIS->layer_height_ranges = layer_height_ranges; %};
void set_layer_height_profile(std::vector<double> profile)
%code%{ THIS->layer_height_profile = profile; %};
size_t total_layer_count();
size_t layer_count();
@ -107,10 +112,31 @@ _constant()
void set_step_started(PrintObjectStep step)
%code%{ THIS->state.set_started(step); %};
void _slice();
void detect_surfaces_type();
void process_external_surfaces();
void discover_vertical_shells();
void bridge_over_infill();
void _make_perimeters();
void _infill();
void _generate_support_material();
void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action)
%code%{
THIS->update_layer_height_profile();
adjust_layer_height_profile(
THIS->slicing_parameters(), THIS->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action));
%};
int generate_layer_height_texture(void *data, int rows, int cols, bool level_of_detail_2nd_level = true)
%code%{
THIS->update_layer_height_profile();
SlicingParameters slicing_params = THIS->slicing_parameters();
RETVAL = generate_layer_height_texture(
slicing_params,
generate_object_layers(slicing_params, THIS->layer_height_profile),
data, rows, cols, level_of_detail_2nd_level);
%};
int ptr()
%code%{ RETVAL = (int)(intptr_t)THIS; %};

View File

@ -1,26 +0,0 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/SupportMaterial.hpp"
%}
%name{Slic3r::Print::SupportMaterial2} class PrintObjectSupportMaterial {
PrintObjectSupportMaterial(PrintObject *print_object);
~PrintObjectSupportMaterial();
void generate(PrintObject *object)
%code{% THIS->generate(*object); %};
};
%package{Slic3r::Print::SupportMaterial};
%{
SV*
MARGIN()
PROTOTYPE:
CODE:
RETVAL = newSVnv(SUPPORT_MATERIAL_MARGIN);
OUTPUT: RETVAL
%}

View File

@ -83,13 +83,12 @@ Surface::polygons()
RETVAL
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

View File

@ -2,6 +2,7 @@
%package{Slic3r::XS};
#include <xsinit.h>
#include "Utils.hpp"
%{
@ -28,6 +29,12 @@ FORK_NAME()
RETVAL = newSVpv(SLIC3R_FORK_NAME, 0);
OUTPUT: RETVAL
void
set_logging_level(level)
unsigned int level;
CODE:
Slic3r::set_logging_level(level);
void
xspp_test_croak_hangs_on_strawberry()
CODE: