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:
commit
b851e04c17
@ -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) = @_;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
@ -130,6 +139,15 @@ sub new {
|
||||
# $self->_camera_type('perspective');
|
||||
$self->_camera_target(Slic3r::Pointf3->new(0,0,0));
|
||||
$self->_camera_distance(0.);
|
||||
|
||||
# Size of a layer height texture, used by a shader to color map the object print layers.
|
||||
$self->{layer_preview_z_texture_width} = 512;
|
||||
$self->{layer_preview_z_texture_height} = 512;
|
||||
$self->{layer_height_edit_band_width} = 2.;
|
||||
$self->{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;
|
||||
|
||||
@ -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;
|
||||
|
||||
# select volume in this 3D canvas
|
||||
if ($self->enable_picking) {
|
||||
$self->deselect_volumes;
|
||||
$self->select_volume($volume_idx);
|
||||
$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);
|
||||
|
||||
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) = @_;
|
||||
@ -720,7 +841,22 @@ sub InitGL {
|
||||
return if $self->init;
|
||||
return unless $self->GetContext;
|
||||
$self->init(1);
|
||||
|
||||
|
||||
my $shader;
|
||||
$shader = $self->{shader} = new Slic3r::GUI::GLShader
|
||||
if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1);
|
||||
if ($self->{shader}) {
|
||||
my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader);
|
||||
print $info if $info;
|
||||
|
||||
($self->{layer_preview_z_texture_id}) = glGenTextures_p(1);
|
||||
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glColor3f(1, 0, 0);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
@ -761,7 +897,14 @@ sub InitGL {
|
||||
glEnable(GL_COLOR_MATERIAL);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
}
|
||||
|
||||
|
||||
sub DestroyGL {
|
||||
my $self = shift;
|
||||
if ($self->init && $self->GetContext) {
|
||||
delete $self->{shader};
|
||||
}
|
||||
}
|
||||
|
||||
sub Render {
|
||||
my ($self, $dc) = @_;
|
||||
|
||||
@ -798,11 +941,15 @@ sub Render {
|
||||
glLightfv_p(GL_LIGHT0, GL_POSITION, -0.5, -0.5, 1, 0);
|
||||
glLightfv_p(GL_LIGHT0, GL_SPECULAR, 0.2, 0.2, 0.2, 1);
|
||||
glLightfv_p(GL_LIGHT0, GL_DIFFUSE, 0.5, 0.5, 0.5, 1);
|
||||
|
||||
# Head light
|
||||
glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0);
|
||||
|
||||
if ($self->enable_picking) {
|
||||
# Render the object for picking.
|
||||
# FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing.
|
||||
# Better to use software ray-casting on a bounding-box hierarchy.
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
glDisable(GL_LIGHTING);
|
||||
$self->draw_volumes(1);
|
||||
glFlush();
|
||||
@ -829,6 +976,7 @@ sub Render {
|
||||
glFlush();
|
||||
glFinish();
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
}
|
||||
|
||||
# draw fixed background
|
||||
@ -928,7 +1076,7 @@ sub Render {
|
||||
|
||||
# draw objects
|
||||
$self->draw_volumes;
|
||||
|
||||
|
||||
# draw cutting plane
|
||||
if (defined $self->cutting_plane_z) {
|
||||
my $plane_z = $self->cutting_plane_z;
|
||||
@ -947,10 +1095,13 @@ sub Render {
|
||||
glEnable(GL_CULL_FACE);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
$self->draw_active_object_annotations;
|
||||
|
||||
glFlush();
|
||||
|
||||
$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')) {
|
||||
@ -1152,6 +1589,19 @@ sub load_object {
|
||||
}
|
||||
|
||||
$instance_idxs ||= [0..$#{$model_object->instances}];
|
||||
|
||||
# Object will have a single common layer height texture for all volumes.
|
||||
my $layer_height_texture_data;
|
||||
my $layer_height_texture_cells;
|
||||
if ($print && $obj_idx < $print->object_count) {
|
||||
# Generate the layer height texture. Allocate data for the 0th and 1st mipmap levels.
|
||||
$layer_height_texture_data = OpenGL::Array->new($self->{layer_preview_z_texture_width}*$self->{layer_preview_z_texture_height}*5, GL_UNSIGNED_BYTE);
|
||||
# $print->get_object($obj_idx)->update_layer_height_profile_from_ranges();
|
||||
$layer_height_texture_cells = $print->get_object($obj_idx)->generate_layer_height_texture(
|
||||
$layer_height_texture_data->ptr,
|
||||
$self->{layer_preview_z_texture_height},
|
||||
$self->{layer_preview_z_texture_width});
|
||||
}
|
||||
|
||||
my @volumes_idx = ();
|
||||
foreach my $volume_idx (0..$#{$model_object->volumes}) {
|
||||
@ -1174,16 +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
184
lib/Slic3r/GUI/GLShader.pm
Normal file
@ -0,0 +1,184 @@
|
||||
############################################################
|
||||
#
|
||||
# Stripped down from the Perl OpenGL::Shader package by Vojtech Bubnik
|
||||
# to only support the GLSL shaders. The original source was not maintained
|
||||
# and did not install properly through the CPAN archive, and it was unnecessary
|
||||
# complex.
|
||||
#
|
||||
# Original copyright:
|
||||
#
|
||||
# Copyright 2007 Graphcomp - ALL RIGHTS RESERVED
|
||||
# Author: Bob "grafman" Free - grafman@graphcomp.com
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the same terms as Perl itself.
|
||||
#
|
||||
############################################################
|
||||
|
||||
package Slic3r::GUI::GLShader;
|
||||
use OpenGL(':all');
|
||||
|
||||
# Avoid cloning this class by the worker threads.
|
||||
sub CLONE_SKIP { 1 }
|
||||
|
||||
# Shader constructor
|
||||
sub new
|
||||
{
|
||||
# Check for required OpenGL extensions
|
||||
return undef if (OpenGL::glpCheckExtension('GL_ARB_shader_objects'));
|
||||
return undef if (OpenGL::glpCheckExtension('GL_ARB_fragment_shader'));
|
||||
return undef if (OpenGL::glpCheckExtension('GL_ARB_vertex_shader'));
|
||||
return undef if (OpenGL::glpCheckExtension('GL_ARB_shading_language_100'));
|
||||
# my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||
# my $glsl_version_ARB = glGetString(GL_SHADING_LANGUAGE_VERSION_ARB );
|
||||
# print "GLSL version: $glsl_version, ARB: $glsl_version_ARB\n";
|
||||
|
||||
my $this = shift;
|
||||
my $class = ref($this) || $this;
|
||||
my($type) = @_;
|
||||
my $self = {type => uc($type)};
|
||||
bless($self,$class);
|
||||
|
||||
# Get GL_SHADING_LANGUAGE_VERSION_ARB
|
||||
my $shader_ver = glGetString(0x8B8C);
|
||||
$shader_ver =~ m|([\d\.]+)|;
|
||||
$self->{version} = $1 || '0';
|
||||
print
|
||||
return $self;
|
||||
}
|
||||
|
||||
# Shader destructor
|
||||
# Must be disabled first
|
||||
sub DESTROY
|
||||
{
|
||||
my($self) = @_;
|
||||
|
||||
if ($self->{program})
|
||||
{
|
||||
glDetachObjectARB($self->{program},$self->{fragment_id}) if ($self->{fragment_id});
|
||||
glDetachObjectARB($self->{program},$self->{vertex_id}) if ($self->{vertex_id});
|
||||
glDeleteProgramsARB_p($self->{program});
|
||||
}
|
||||
|
||||
glDeleteProgramsARB_p($self->{fragment_id}) if ($self->{fragment_id});
|
||||
glDeleteProgramsARB_p($self->{vertex_id}) if ($self->{vertex_id});
|
||||
}
|
||||
|
||||
|
||||
# Load shader strings
|
||||
sub Load
|
||||
{
|
||||
my($self,$fragment,$vertex) = @_;
|
||||
|
||||
# Load fragment code
|
||||
if ($fragment)
|
||||
{
|
||||
$self->{fragment_id} = glCreateShaderObjectARB(GL_FRAGMENT_SHADER);
|
||||
return undef if (!$self->{fragment_id});
|
||||
|
||||
glShaderSourceARB_p($self->{fragment_id}, $fragment);
|
||||
#my $frag = glGetShaderSourceARB_p($self->{fragment_id});
|
||||
#print STDERR "Loaded fragment:\n$frag\n";
|
||||
|
||||
glCompileShaderARB($self->{fragment_id});
|
||||
my $stat = glGetInfoLogARB_p($self->{fragment_id});
|
||||
return "Fragment shader: $stat" if ($stat);
|
||||
}
|
||||
|
||||
# Load vertex code
|
||||
if ($vertex)
|
||||
{
|
||||
$self->{vertex_id} = glCreateShaderObjectARB(GL_VERTEX_SHADER);
|
||||
return undef if (!$self->{vertex_id});
|
||||
|
||||
glShaderSourceARB_p($self->{vertex_id}, $vertex);
|
||||
#my $vert = glGetShaderSourceARB_p($self->{vertex_id});
|
||||
#print STDERR "Loaded vertex:\n$vert\n";
|
||||
|
||||
glCompileShaderARB($self->{vertex_id});
|
||||
$stat = glGetInfoLogARB_p($self->{vertex_id});
|
||||
return "Vertex shader: $stat" if ($stat);
|
||||
}
|
||||
|
||||
|
||||
# Link shaders
|
||||
my $sp = glCreateProgramObjectARB();
|
||||
glAttachObjectARB($sp, $self->{fragment_id}) if ($fragment);
|
||||
glAttachObjectARB($sp, $self->{vertex_id}) if ($vertex);
|
||||
glLinkProgramARB($sp);
|
||||
my $linked = glGetObjectParameterivARB_p($sp, GL_OBJECT_LINK_STATUS_ARB);
|
||||
if (!$linked)
|
||||
{
|
||||
$stat = glGetInfoLogARB_p($sp);
|
||||
#print STDERR "Load shader: $stat\n";
|
||||
return "Link shader: $stat" if ($stat);
|
||||
return 'Unable to link shader';
|
||||
}
|
||||
|
||||
$self->{program} = $sp;
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
# Enable shader
|
||||
sub Enable
|
||||
{
|
||||
my($self) = @_;
|
||||
glUseProgramObjectARB($self->{program}) if ($self->{program});
|
||||
}
|
||||
|
||||
# Disable shader
|
||||
sub Disable
|
||||
{
|
||||
my($self) = @_;
|
||||
glUseProgramObjectARB(0) if ($self->{program});
|
||||
}
|
||||
|
||||
# Return shader vertex attribute ID
|
||||
sub MapAttr
|
||||
{
|
||||
my($self,$attr) = @_;
|
||||
return undef if (!$self->{program});
|
||||
my $id = glGetAttribLocationARB_p($self->{program},$attr);
|
||||
return undef if ($id < 0);
|
||||
return $id;
|
||||
}
|
||||
|
||||
# Return shader uniform variable ID
|
||||
sub Map
|
||||
{
|
||||
my($self,$var) = @_;
|
||||
return undef if (!$self->{program});
|
||||
my $id = glGetUniformLocationARB_p($self->{program},$var);
|
||||
return undef if ($id < 0);
|
||||
return $id;
|
||||
}
|
||||
|
||||
# Set shader vector
|
||||
sub SetVector
|
||||
{
|
||||
my($self,$var,@values) = @_;
|
||||
|
||||
my $id = $self->Map($var);
|
||||
return 'Unable to map $var' if (!defined($id));
|
||||
|
||||
my $count = scalar(@values);
|
||||
eval('glUniform'.$count.'fARB($id,@values)');
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
# Set shader 4x4 matrix
|
||||
sub SetMatrix
|
||||
{
|
||||
my($self,$var,$oga) = @_;
|
||||
|
||||
my $id = $self->Map($var);
|
||||
return 'Unable to map $var' if (!defined($id));
|
||||
|
||||
glUniformMatrix4fvARB_c($id,1,0,$oga->ptr());
|
||||
return '';
|
||||
}
|
||||
|
||||
1;
|
||||
__END__
|
@ -42,6 +42,11 @@ sub new {
|
||||
$self->_init_tabpanel;
|
||||
$self->_init_menubar;
|
||||
|
||||
# set default tooltip timer in msec
|
||||
# SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values
|
||||
# (SetAutoPop is not available on GTK.)
|
||||
eval { Wx::ToolTip::SetAutoPop(32767) };
|
||||
|
||||
# initialize status bar
|
||||
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1);
|
||||
$self->{statusbar}->SetStatusText("Version $Slic3r::VERSION - Remember to check for updates at http://github.com/prusa3d/slic3r/releases");
|
||||
@ -292,7 +297,7 @@ sub _init_menubar {
|
||||
# View menu
|
||||
if (!$self->{no_plater}) {
|
||||
$self->{viewMenu} = Wx::Menu->new;
|
||||
$self->_append_menu_item($self->{viewMenu}, "Default", 'Default View', sub { $self->select_view('default'); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Iso" , 'Iso View' , sub { $self->select_view('iso' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Top" , 'Top View' , sub { $self->select_view('top' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Bottom" , 'Bottom View' , sub { $self->select_view('bottom' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, "Front" , 'Front View' , sub { $self->select_view('front' ); });
|
||||
|
@ -8,10 +8,11 @@ use utf8;
|
||||
use File::Basename qw(basename dirname);
|
||||
use List::Util qw(sum first max);
|
||||
use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad);
|
||||
use LWP::UserAgent;
|
||||
use threads::shared qw(shared_clone);
|
||||
use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
|
||||
:panel :sizer :toolbar :window wxTheApp :notebook :combobox);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
|
||||
:panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
|
||||
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
|
||||
EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED);
|
||||
use base 'Wx::Panel';
|
||||
@ -30,6 +31,7 @@ use constant TB_SCALE => &Wx::NewId;
|
||||
use constant TB_SPLIT => &Wx::NewId;
|
||||
use constant TB_CUT => &Wx::NewId;
|
||||
use constant TB_SETTINGS => &Wx::NewId;
|
||||
use constant TB_LAYER_EDITING => &Wx::NewId;
|
||||
|
||||
# package variables to avoid passing lexicals to threads
|
||||
our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType;
|
||||
@ -94,7 +96,7 @@ sub new {
|
||||
|
||||
# Initialize 3D plater
|
||||
if ($Slic3r::GUI::have_OpenGL) {
|
||||
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config});
|
||||
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config});
|
||||
$self->{preview_notebook}->AddPage($self->{canvas3D}, '3D');
|
||||
$self->{canvas3D}->set_on_select_object($on_select_object);
|
||||
$self->{canvas3D}->set_on_double_click($on_double_click);
|
||||
@ -154,6 +156,10 @@ sub new {
|
||||
$self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), '');
|
||||
$self->{htoolbar}->AddSeparator;
|
||||
$self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), '');
|
||||
|
||||
# FIXME add a button for layer editing
|
||||
$self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new($Slic3r::var->("delete.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, undef, 'Layer Editing')
|
||||
if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1);
|
||||
} else {
|
||||
my %tbar_buttons = (
|
||||
add => "Add…",
|
||||
@ -168,12 +174,17 @@ sub new {
|
||||
split => "Split",
|
||||
cut => "Cut…",
|
||||
settings => "Settings…",
|
||||
layer_editing => "Layer editing",
|
||||
);
|
||||
$self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) {
|
||||
$self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
|
||||
$self->{btoolbar}->Add($self->{"btn_$_"});
|
||||
}
|
||||
if (defined($ENV{'SLIC3R_EXPERIMENTAL'}) && $ENV{'SLIC3R_EXPERIMENTAL'} == 1) {
|
||||
$self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
|
||||
$self->{btoolbar}->Add($self->{"btn_layer_editing"});
|
||||
}
|
||||
}
|
||||
|
||||
$self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize,
|
||||
@ -256,6 +267,11 @@ sub new {
|
||||
EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; });
|
||||
EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog });
|
||||
EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog });
|
||||
EVT_TOOL($self, TB_LAYER_EDITING, sub {
|
||||
my $state = $self->{canvas3D}->layer_editing_enabled;
|
||||
$self->{htoolbar}->ToggleTool(TB_LAYER_EDITING, ! $state);
|
||||
$self->on_layer_editing_toggled(! $state);
|
||||
});
|
||||
} else {
|
||||
EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; });
|
||||
EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove
|
||||
@ -269,6 +285,7 @@ sub new {
|
||||
EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; });
|
||||
EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog });
|
||||
EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog });
|
||||
EVT_TOGGLEBUTTON($self, $self->{btn_layer_editing}, sub { $self->on_layer_editing_toggled($self->{btn_layer_editing}->GetValue); });
|
||||
}
|
||||
|
||||
$_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
|
||||
@ -464,6 +481,12 @@ sub _on_select_preset {
|
||||
$self->on_config_change($self->GetFrame->config);
|
||||
}
|
||||
|
||||
sub on_layer_editing_toggled {
|
||||
my ($self, $new_state) = @_;
|
||||
$self->{canvas3D}->layer_editing_enabled($new_state);
|
||||
$self->{canvas3D}->update;
|
||||
}
|
||||
|
||||
sub GetFrame {
|
||||
my ($self) = @_;
|
||||
return &Wx::GetTopLevelParent($self);
|
||||
@ -1481,6 +1504,8 @@ sub on_thumbnail_made {
|
||||
# (i.e. when an object is added/removed/moved/rotated/scaled)
|
||||
sub update {
|
||||
my ($self, $force_autocenter) = @_;
|
||||
|
||||
print "Platter - update\n";
|
||||
|
||||
if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) {
|
||||
$self->{model}->center_instances_around_point($self->bed_centerf);
|
||||
@ -1679,7 +1704,7 @@ sub object_list_changed {
|
||||
my $have_objects = @{$self->{objects}} ? 1 : 0;
|
||||
my $method = $have_objects ? 'Enable' : 'Disable';
|
||||
$self->{"btn_$_"}->$method
|
||||
for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode);
|
||||
for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode layer_editing);
|
||||
|
||||
if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) {
|
||||
$self->{btn_reslice}->Disable;
|
||||
@ -1712,6 +1737,7 @@ sub selection_changed {
|
||||
if ($self->{object_info_size}) { # have we already loaded the info pane?
|
||||
if ($have_sel) {
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
$model_object->print_info;
|
||||
my $model_instance = $model_object->instances->[0];
|
||||
$self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size}));
|
||||
$self->{object_info_materials}->SetLabel($model_object->materials_count);
|
||||
|
@ -173,7 +173,7 @@ sub repaint {
|
||||
|
||||
# if sequential printing is enabled and we have more than one object, draw clearance area
|
||||
if ($self->{config}->complete_objects && (map @{$_->instances}, @{$self->{model}->objects}) > 1) {
|
||||
my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), 1, JT_ROUND, scale(0.1))};
|
||||
my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), JT_ROUND, scale(0.1))};
|
||||
$dc->SetPen($self->{clearance_pen});
|
||||
$dc->SetBrush($self->{transparent_brush});
|
||||
$dc->DrawPolygon($self->scaled_points_to_pixel($clearance, 1), 0, 0);
|
||||
@ -185,7 +185,7 @@ sub repaint {
|
||||
if (@{$self->{objects}} && $self->{config}->skirts) {
|
||||
my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$self->{objects}};
|
||||
if (@points >= 3) {
|
||||
my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), 1, JT_ROUND, scale(0.1))};
|
||||
my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), JT_ROUND, scale(0.1))};
|
||||
$dc->SetPen($self->{skirt_pen});
|
||||
$dc->SetBrush($self->{transparent_brush});
|
||||
$dc->DrawPolygon($self->scaled_points_to_pixel($convex_hull, 1), 0, 0);
|
||||
|
@ -12,7 +12,7 @@ use base qw(Slic3r::GUI::3DScene Class::Accessor);
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $objects, $model, $config) = @_;
|
||||
my ($parent, $objects, $model, $print, $config) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent);
|
||||
$self->enable_picking(1);
|
||||
@ -22,6 +22,7 @@ sub new {
|
||||
|
||||
$self->{objects} = $objects;
|
||||
$self->{model} = $model;
|
||||
$self->{print} = $print;
|
||||
$self->{config} = $config;
|
||||
$self->{on_select_object} = sub {};
|
||||
$self->{on_instances_moved} = sub {};
|
||||
@ -31,7 +32,7 @@ sub new {
|
||||
|
||||
my $obj_idx = undef;
|
||||
if ($volume_idx != -1) {
|
||||
$obj_idx = $self->object_idx($volume_idx);
|
||||
$obj_idx = $self->volumes->[$volume_idx]->object_idx;
|
||||
}
|
||||
$self->{on_select_object}->($obj_idx)
|
||||
if $self->{on_select_object};
|
||||
@ -42,8 +43,8 @@ sub new {
|
||||
my %done = (); # prevent moving instances twice
|
||||
foreach my $volume_idx (@volume_idxs) {
|
||||
my $volume = $self->volumes->[$volume_idx];
|
||||
my $obj_idx = $self->object_idx($volume_idx);
|
||||
my $instance_idx = $self->instance_idx($volume_idx);
|
||||
my $obj_idx = $volume->object_idx;
|
||||
my $instance_idx = $volume->instance_idx;
|
||||
next if $done{"${obj_idx}_${instance_idx}"};
|
||||
$done{"${obj_idx}_${instance_idx}"} = 1;
|
||||
|
||||
@ -89,7 +90,7 @@ sub update {
|
||||
$self->update_bed_size;
|
||||
|
||||
foreach my $obj_idx (0..$#{$self->{model}->objects}) {
|
||||
my @volume_idxs = $self->load_object($self->{model}, $obj_idx);
|
||||
my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx);
|
||||
|
||||
if ($self->{objects}[$obj_idx]->selected) {
|
||||
$self->select_volume($_) for @volume_idxs;
|
||||
|
@ -114,7 +114,7 @@ sub new {
|
||||
my $canvas;
|
||||
if ($Slic3r::GUI::have_OpenGL) {
|
||||
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
|
||||
$canvas->load_object($self->{model_object}, undef, [0]);
|
||||
$canvas->load_object($self->{model_object}, undef, undef, [0]);
|
||||
$canvas->set_auto_bed_shape;
|
||||
$canvas->SetSize([500,500]);
|
||||
$canvas->SetMinSize($canvas->GetSize);
|
||||
@ -244,7 +244,7 @@ sub _update {
|
||||
}
|
||||
|
||||
$self->{canvas}->reset_objects;
|
||||
$self->{canvas}->load_object($_, undef, [0]) for @objects;
|
||||
$self->{canvas}->load_object($_, undef, undef, [0]) for @objects;
|
||||
$self->{canvas}->SetCuttingPlane(
|
||||
$self->{cut_options}{z},
|
||||
[@expolygons],
|
||||
|
@ -22,6 +22,7 @@ sub new {
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
|
||||
my $object = $self->{model_object} = $params{model_object};
|
||||
my $print_object = $self->{print_object} = $params{print_object};
|
||||
|
||||
# create TreeCtrl
|
||||
my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100],
|
||||
@ -82,7 +83,7 @@ sub new {
|
||||
$self->reload_tree($canvas->volume_idx($volume_idx));
|
||||
});
|
||||
|
||||
$canvas->load_object($self->{model_object}, undef, [0]);
|
||||
$canvas->load_object($self->{model_object}, undef, undef, [0]);
|
||||
$canvas->set_auto_bed_shape;
|
||||
$canvas->SetSize([500,500]);
|
||||
$canvas->zoom_to_volumes;
|
||||
|
@ -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);
|
||||
|
@ -5,9 +5,9 @@ use warnings;
|
||||
require Exporter;
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT_OK = qw(offset offset_ex
|
||||
diff_ex diff union_ex intersection_ex xor_ex JT_ROUND JT_MITER
|
||||
JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex
|
||||
intersection intersection_pl diff_pl union CLIPPER_OFFSET_SCALE
|
||||
union_pt_chained diff_ppl intersection_ppl);
|
||||
diff_ex diff union_ex intersection_ex JT_ROUND JT_MITER
|
||||
JT_SQUARE is_counter_clockwise offset2 offset2_ex
|
||||
intersection intersection_pl diff_pl union
|
||||
union_pt_chained);
|
||||
|
||||
1;
|
||||
|
@ -41,8 +41,12 @@ sub size {
|
||||
sub process {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->status_cb->(20, "Generating perimeters");
|
||||
$_->make_perimeters for @{$self->objects};
|
||||
|
||||
$self->status_cb->(70, "Infilling layers");
|
||||
$_->infill for @{$self->objects};
|
||||
|
||||
$_->generate_support_material for @{$self->objects};
|
||||
$self->make_skirt;
|
||||
$self->make_brim; # must come after make_skirt
|
||||
@ -276,7 +280,7 @@ sub make_skirt {
|
||||
my $distance = scale max($self->config->skirt_distance, $self->config->brim_width);
|
||||
for (my $i = $skirts; $i > 0; $i--) {
|
||||
$distance += scale $spacing;
|
||||
my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0];
|
||||
my $loop = offset([$convex_hull], $distance, JT_ROUND, scale(0.1))->[0];
|
||||
my $eloop = Slic3r::ExtrusionLoop->new_from_paths(
|
||||
Slic3r::ExtrusionPath->new(
|
||||
polyline => Slic3r::Polygon->new(@$loop)->split_at_first_point,
|
||||
@ -369,7 +373,7 @@ sub make_brim {
|
||||
# -0.5 because islands are not represented by their centerlines
|
||||
# (first offset more, then step back - reverse order than the one used for
|
||||
# perimeters because here we're offsetting outwards)
|
||||
push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, 100000, JT_SQUARE)};
|
||||
push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, JT_SQUARE)};
|
||||
}
|
||||
|
||||
$self->brim->append(map Slic3r::ExtrusionLoop->new_from_paths(
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
# 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++;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 = ();
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
27
xs/Build.PL
27
xs/Build.PL
@ -117,17 +117,21 @@ if (defined $ENV{BOOST_LIBRARYDIR}) {
|
||||
|
||||
# In order to generate the -l switches we need to know how Boost libraries are named
|
||||
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',
|
||||
|
10
xs/MANIFEST
10
xs/MANIFEST
@ -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
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <boost/predef/detail/endian_compat.h>
|
||||
#include <boost/detail/endian.hpp>
|
||||
|
||||
#ifndef BOOST_LITTLE_ENDIAN
|
||||
#error "admesh works correctly on little endian machines only!"
|
||||
|
@ -43,23 +43,8 @@ stl_open(stl_file *stl, char *file) {
|
||||
|
||||
void
|
||||
stl_initialize(stl_file *stl) {
|
||||
stl->error = 0;
|
||||
stl->stats.degenerate_facets = 0;
|
||||
stl->stats.edges_fixed = 0;
|
||||
stl->stats.facets_added = 0;
|
||||
stl->stats.facets_removed = 0;
|
||||
stl->stats.facets_reversed = 0;
|
||||
stl->stats.normals_fixed = 0;
|
||||
stl->stats.number_of_parts = 0;
|
||||
stl->stats.original_num_facets = 0;
|
||||
stl->stats.number_of_facets = 0;
|
||||
stl->stats.facets_malloced = 0;
|
||||
memset(stl, 0, sizeof(stl_file));
|
||||
stl->stats.volume = -1.0;
|
||||
|
||||
stl->neighbors_start = NULL;
|
||||
stl->facet_start = NULL;
|
||||
stl->v_indices = NULL;
|
||||
stl->v_shared = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
@ -270,6 +255,7 @@ stl_read(stl_file *stl, int first_facet, int first) {
|
||||
rewind(stl->fp);
|
||||
}
|
||||
|
||||
char normal_buf[3][32];
|
||||
for(i = first_facet; i < stl->stats.number_of_facets; i++) {
|
||||
if(stl->stats.type == binary)
|
||||
/* Read a single facet from a binary .STL file */
|
||||
@ -287,17 +273,25 @@ stl_read(stl_file *stl, int first_facet, int first) {
|
||||
fscanf(stl->fp, "endsolid\n");
|
||||
fscanf(stl->fp, "solid%*[^\n]\n"); // name might contain spaces so %*s doesn't work and it also can be empty (just "solid")
|
||||
|
||||
if((fscanf(stl->fp, " facet normal %f %f %f\n", &facet.normal.x, &facet.normal.y, &facet.normal.z) + \
|
||||
fscanf(stl->fp, " outer loop\n") + \
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z) + \
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z) + \
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z) + \
|
||||
fscanf(stl->fp, " endloop\n") + \
|
||||
if((fscanf(stl->fp, " facet normal %31s %31s %31s\n", normal_buf[0], normal_buf[1], normal_buf[2]) +
|
||||
fscanf(stl->fp, " outer loop\n") +
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z) +
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z) +
|
||||
fscanf(stl->fp, " vertex %f %f %f\n", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z) +
|
||||
fscanf(stl->fp, " endloop\n") +
|
||||
fscanf(stl->fp, " endfacet\n")) != 12) {
|
||||
perror("Something is syntactically very wrong with this ASCII STL!");
|
||||
stl->error = 1;
|
||||
return;
|
||||
}
|
||||
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
|
||||
if (sscanf(normal_buf[0], "%f", &facet.normal.x) != 1 ||
|
||||
sscanf(normal_buf[1], "%f", &facet.normal.y) != 1 ||
|
||||
sscanf(normal_buf[2], "%f", &facet.normal.z) != 1) {
|
||||
// Normal was mangled. Maybe denormals or "not a number" were stored?
|
||||
// Just reset the normal and silently ignore it.
|
||||
memset(&facet.normal, 0, sizeof(facet.normal));
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -153,17 +153,35 @@ private:
|
||||
PolyNode* GetNextSiblingUp() const;
|
||||
void AddChild(PolyNode& child);
|
||||
friend class Clipper; //to access Index
|
||||
friend class ClipperOffset;
|
||||
friend class ClipperOffset;
|
||||
friend class PolyTree; //to implement the PolyTree::move operator
|
||||
};
|
||||
|
||||
class PolyTree: public PolyNode
|
||||
{
|
||||
public:
|
||||
~PolyTree(){Clear();};
|
||||
PolyTree() {}
|
||||
PolyTree(PolyTree &&src) { *this = std::move(src); }
|
||||
virtual ~PolyTree(){Clear();};
|
||||
PolyTree& operator=(PolyTree &&src) {
|
||||
AllNodes = std::move(src.AllNodes);
|
||||
Contour = std::move(src.Contour);
|
||||
Childs = std::move(src.Childs);
|
||||
Parent = nullptr;
|
||||
Index = src.Index;
|
||||
m_IsOpen = src.m_IsOpen;
|
||||
m_jointype = src.m_jointype;
|
||||
m_endtype = src.m_endtype;
|
||||
for (size_t i = 0; i < Childs.size(); ++ i)
|
||||
Childs[i]->Parent = this;
|
||||
return *this;
|
||||
}
|
||||
PolyNode* GetFirst() const;
|
||||
void Clear();
|
||||
int Total() const;
|
||||
private:
|
||||
PolyTree(const PolyTree &src) = delete;
|
||||
PolyTree& operator=(const PolyTree &src) = delete;
|
||||
PolyNodes AllNodes;
|
||||
friend class Clipper; //to access AllNodes
|
||||
};
|
||||
|
@ -277,4 +277,26 @@ BoundingBoxBase<PointClass>::overlap(const BoundingBoxBase<PointClass> &other) c
|
||||
template bool BoundingBoxBase<Point>::overlap(const BoundingBoxBase<Point> &point) const;
|
||||
template bool BoundingBoxBase<Pointf>::overlap(const BoundingBoxBase<Pointf> &point) const;
|
||||
|
||||
|
||||
// Align a coordinate to a grid. The coordinate may be negative,
|
||||
// the aligned value will never be bigger than the original one.
|
||||
static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
|
||||
// Current C++ standard defines the result of integer division to be rounded to zero,
|
||||
// for both positive and negative numbers. Here we want to round down for negative
|
||||
// numbers as well.
|
||||
coord_t aligned = (coord < 0) ?
|
||||
((coord - spacing + 1) / spacing) * spacing :
|
||||
(coord / spacing) * spacing;
|
||||
assert(aligned <= coord);
|
||||
return aligned;
|
||||
}
|
||||
|
||||
void BoundingBox::align_to_grid(const coord_t cell_size)
|
||||
{
|
||||
if (this->defined) {
|
||||
min.x = _align_to_grid(min.x, cell_size);
|
||||
min.y = _align_to_grid(min.y, cell_size);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -65,6 +65,9 @@ class BoundingBox : public BoundingBoxBase<Point>
|
||||
BoundingBox rotated(double angle, const Point ¢er) const;
|
||||
void rotate(double angle) { (*this) = this->rotated(angle); }
|
||||
void rotate(double angle, const Point ¢er) { (*this) = this->rotated(angle, center); }
|
||||
// Align the min corner to a grid of cell_size x cell_size cells,
|
||||
// to encompass the original bounding box.
|
||||
void align_to_grid(const coord_t cell_size);
|
||||
|
||||
BoundingBox() : BoundingBoxBase<Point>() {};
|
||||
BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase<Point>(pmin, pmax) {};
|
||||
|
@ -40,13 +40,13 @@ void BridgeDetector::initialize()
|
||||
this->angle = -1.;
|
||||
|
||||
// Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
|
||||
Polygons grown = offset(this->expolygons, float(this->spacing));
|
||||
Polygons grown = offset(to_polygons(this->expolygons), float(this->spacing));
|
||||
|
||||
// Detect possible anchoring edges of this bridging region.
|
||||
// Detect what edges lie on lower slices by turning bridge contour and holes
|
||||
// into polylines and then clipping them with each lower slice's contour.
|
||||
// Currently _edges are only used to set a candidate direction of the bridge (see bridge_direction_candidates()).
|
||||
intersection(to_polylines(grown), this->lower_slices.contours(), &this->_edges);
|
||||
this->_edges = intersection_pl(to_polylines(grown), this->lower_slices.contours());
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf(" bridge has " PRINTF_ZU " support(s)\n", this->_edges.size());
|
||||
@ -117,7 +117,7 @@ BridgeDetector::detect_angle()
|
||||
double total_length = 0;
|
||||
double max_length = 0;
|
||||
{
|
||||
Lines clipped_lines = intersection(lines, clip_area);
|
||||
Lines clipped_lines = intersection_ln(lines, clip_area);
|
||||
for (size_t i = 0; i < clipped_lines.size(); ++i) {
|
||||
const Line &line = clipped_lines[i];
|
||||
if (expolygons_contain(this->_anchor_regions, line.a) && expolygons_contain(this->_anchor_regions, line.b)) {
|
||||
@ -203,76 +203,72 @@ std::vector<double> BridgeDetector::bridge_direction_candidates() const
|
||||
return angles;
|
||||
}
|
||||
|
||||
void
|
||||
BridgeDetector::coverage(double angle, Polygons* coverage) const
|
||||
Polygons BridgeDetector::coverage(double angle) const
|
||||
{
|
||||
if (angle == -1) angle = this->angle;
|
||||
if (angle == -1) return;
|
||||
if (angle == -1)
|
||||
angle = this->angle;
|
||||
|
||||
// Get anchors, convert them to Polygons and rotate them.
|
||||
Polygons anchors = to_polygons(this->_anchor_regions);
|
||||
polygons_rotate(anchors, PI/2.0 - angle);
|
||||
|
||||
Polygons covered;
|
||||
for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly)
|
||||
{
|
||||
// Clone our expolygon and rotate it so that we work with vertical lines.
|
||||
ExPolygon expolygon = *it_expoly;
|
||||
expolygon.rotate(PI/2.0 - angle);
|
||||
|
||||
if (angle != -1) {
|
||||
|
||||
// Get anchors, convert them to Polygons and rotate them.
|
||||
Polygons anchors = to_polygons(this->_anchor_regions);
|
||||
polygons_rotate(anchors, PI/2.0 - angle);
|
||||
|
||||
/* Outset the bridge expolygon by half the amount we used for detecting anchors;
|
||||
we'll use this one to generate our trapezoids and be sure that their vertices
|
||||
are inside the anchors and not on their contours leading to false negatives. */
|
||||
ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing));
|
||||
|
||||
// Compute trapezoids according to a vertical orientation
|
||||
Polygons trapezoids;
|
||||
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
|
||||
it->get_trapezoids2(&trapezoids, PI/2.0);
|
||||
|
||||
for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
|
||||
Lines supported = intersection(trapezoid->lines(), anchors);
|
||||
size_t n_supported = 0;
|
||||
// not nice, we need a more robust non-numeric check
|
||||
for (size_t i = 0; i < supported.size(); ++i)
|
||||
if (supported[i].length() >= this->spacing)
|
||||
++ n_supported;
|
||||
if (n_supported >= 2)
|
||||
covered.push_back(STDMOVE(*trapezoid));
|
||||
for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly)
|
||||
{
|
||||
// Clone our expolygon and rotate it so that we work with vertical lines.
|
||||
ExPolygon expolygon = *it_expoly;
|
||||
expolygon.rotate(PI/2.0 - angle);
|
||||
|
||||
/* Outset the bridge expolygon by half the amount we used for detecting anchors;
|
||||
we'll use this one to generate our trapezoids and be sure that their vertices
|
||||
are inside the anchors and not on their contours leading to false negatives. */
|
||||
ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing));
|
||||
|
||||
// Compute trapezoids according to a vertical orientation
|
||||
Polygons trapezoids;
|
||||
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
|
||||
it->get_trapezoids2(&trapezoids, PI/2.0);
|
||||
|
||||
for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
|
||||
Lines supported = intersection_ln(trapezoid->lines(), anchors);
|
||||
size_t n_supported = 0;
|
||||
// not nice, we need a more robust non-numeric check
|
||||
for (size_t i = 0; i < supported.size(); ++i)
|
||||
if (supported[i].length() >= this->spacing)
|
||||
++ n_supported;
|
||||
if (n_supported >= 2)
|
||||
covered.push_back(STDMOVE(*trapezoid));
|
||||
}
|
||||
}
|
||||
|
||||
// Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
|
||||
// instead of exact overlaps.
|
||||
covered = union_(covered);
|
||||
|
||||
// Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
|
||||
polygons_rotate(covered, -(PI/2.0 - angle));
|
||||
covered = intersection(covered, to_polygons(this->expolygons));
|
||||
|
||||
/*
|
||||
if (0) {
|
||||
my @lines = map @{$_->lines}, @$trapezoids;
|
||||
$_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
|
||||
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"coverage_" . rad2deg($angle) . ".svg",
|
||||
expolygons => [$self->expolygon],
|
||||
green_expolygons => $self->_anchor_regions,
|
||||
red_expolygons => $coverage,
|
||||
lines => \@lines,
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
|
||||
// instead of exact overlaps.
|
||||
covered = union_(covered);
|
||||
|
||||
// Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
|
||||
polygons_rotate(covered, -(PI/2.0 - angle));
|
||||
intersection(covered, to_polygons(this->expolygons), coverage);
|
||||
|
||||
/*
|
||||
if (0) {
|
||||
my @lines = map @{$_->lines}, @$trapezoids;
|
||||
$_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
|
||||
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"coverage_" . rad2deg($angle) . ".svg",
|
||||
expolygons => [$self->expolygon],
|
||||
green_expolygons => $self->_anchor_regions,
|
||||
red_expolygons => $coverage,
|
||||
lines => \@lines,
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
Polygons
|
||||
BridgeDetector::coverage(double angle) const
|
||||
{
|
||||
Polygons pp;
|
||||
this->coverage(angle, &pp);
|
||||
return pp;
|
||||
return covered;
|
||||
}
|
||||
|
||||
/* This method returns the bridge edges (as polylines) that are not supported
|
||||
@ -288,9 +284,7 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
|
||||
|
||||
for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) {
|
||||
// get unsupported bridge edges (both contour and holes)
|
||||
Polylines unuspported_polylines;
|
||||
diff(to_polylines(*it_expoly), grown_lower, &unuspported_polylines);
|
||||
Lines unsupported_lines = to_lines(unuspported_polylines);
|
||||
Lines unsupported_lines = to_lines(diff_pl(to_polylines(*it_expoly), grown_lower));
|
||||
/* Split into individual segments and filter out edges parallel to the bridging angle
|
||||
TODO: angle tolerance should probably be based on segment length and flow width,
|
||||
so that we build supports whenever there's a chance that at least one or two bridge
|
||||
|
@ -32,7 +32,6 @@ public:
|
||||
BridgeDetector(ExPolygon _expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
|
||||
BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
|
||||
bool detect_angle();
|
||||
void coverage(double angle, Polygons* coverage) const;
|
||||
Polygons coverage(double angle = -1) const;
|
||||
void unsupported_edges(double angle, Polylines* unsupported) const;
|
||||
Polylines unsupported_edges(double angle = -1) const;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,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
|
||||
#endif
|
@ -1,6 +1,7 @@
|
||||
#ifndef slic3r_Config_hpp_
|
||||
#define slic3r_Config_hpp_
|
||||
|
||||
#include <assert.h>
|
||||
#include <map>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
@ -73,11 +74,8 @@ class ConfigOptionVector : public ConfigOptionVectorBase
|
||||
};
|
||||
|
||||
T get_at(size_t i) const {
|
||||
try {
|
||||
return this->values.at(i);
|
||||
} catch (const std::out_of_range& oor) {
|
||||
return this->values.front();
|
||||
}
|
||||
assert(! this->values.empty());
|
||||
return (i < this->values.size()) ? this->values[i] : this->values.front();
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <float.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
#include <wx/image.h>
|
||||
@ -109,7 +110,6 @@ void EdgeGrid::Grid::create(const ExPolygonCollection &expolygons, coord_t resol
|
||||
void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
|
||||
{
|
||||
// 1) Measure the bounding box.
|
||||
m_bbox.defined = false;
|
||||
for (size_t i = 0; i < m_contours.size(); ++ i) {
|
||||
const Slic3r::Points &pts = *m_contours[i];
|
||||
for (size_t j = 0; j < pts.size(); ++ j)
|
||||
@ -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 */
|
||||
}
|
||||
@ -1118,7 +1120,7 @@ float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radius, coordf_t &result_min_dist, bool *pon_segment) const {
|
||||
BoundingBox bbox;
|
||||
bbox.min = bbox.max = Point(pt.x - m_bbox.min.x, pt.y - m_bbox.min.y);
|
||||
@ -1222,6 +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,9 +1386,9 @@ 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[2] = 255 - is;
|
||||
pxl[0] = 255;
|
||||
pxl[1] = 0;
|
||||
pxl[2] = 255 - is;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -803,7 +803,7 @@ void gcode_spread_points(
|
||||
const Cell &cell = cells[i];
|
||||
acc[cell.idx.y()][cell.idx.x()] = (1.f - cell.fraction_covered) * cell.volume + cell.fraction_covered * cell.area * height_avg;
|
||||
}
|
||||
} else if (simulationType == ExtrusionSimulationSpreadExcess) {
|
||||
} else if (simulationType == Slic3r::ExtrusionSimulationSpreadExcess) {
|
||||
// The volume under the circle does not fit.
|
||||
// 1) Fill the underfilled cells and remove them from the list.
|
||||
float volume_borrowed_total = 0.;
|
||||
|
@ -13,7 +13,7 @@ enum ExtrusionSimulationType
|
||||
ExtrusionSimulationDontSpread,
|
||||
ExtrisopmSimulationSpreadNotOverfilled,
|
||||
ExtrusionSimulationSpreadFull,
|
||||
ExtrusionSimulationSpreadExcess,
|
||||
ExtrusionSimulationSpreadExcess
|
||||
};
|
||||
|
||||
// An opaque class, to keep the boost stuff away from the header.
|
||||
|
@ -246,22 +246,21 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
|
||||
flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow());
|
||||
}
|
||||
|
||||
// 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
|
||||
// thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection
|
||||
// Unpacks the collection, creates multiple collections per path.
|
||||
|
@ -168,7 +168,7 @@ void Fill3DHoneycomb::_fill_surface_single(
|
||||
it->translate(bb.min.x, bb.min.y);
|
||||
|
||||
// clip pattern to boundaries
|
||||
intersection(polylines, (Polygons)expolygon, &polylines);
|
||||
polylines = intersection_pl(polylines, (Polygons)expolygon);
|
||||
|
||||
// connect lines
|
||||
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||
|
@ -45,8 +45,7 @@ Fill* Fill::new_from_type(const std::string &type)
|
||||
Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
// Perform offset.
|
||||
Slic3r::ExPolygons expp;
|
||||
offset(surface->expolygon, &expp, -0.5*scale_(this->spacing));
|
||||
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(-0.5*scale_(this->spacing)));
|
||||
// Create the infills for each of the regions.
|
||||
Polylines polylines_out;
|
||||
for (size_t i = 0; i < expp.size(); ++ i)
|
||||
|
@ -33,7 +33,7 @@ void FillConcentric::_fill_surface_single(
|
||||
|
||||
// generate paths from the outermost to the innermost, to avoid
|
||||
// adhesion problems of the first central tiny loops
|
||||
union_pt_chained(loops, &loops, false);
|
||||
loops = union_pt_chained(loops, false);
|
||||
|
||||
// split paths using a nearest neighbor search
|
||||
size_t iPathFirst = polylines_out.size();
|
||||
|
@ -93,7 +93,7 @@ void FillHoneycomb::_fill_surface_single(
|
||||
Polylines p;
|
||||
for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
|
||||
p.push_back((Polyline)(*it));
|
||||
intersection(p, (Polygons)expolygon, &paths);
|
||||
paths = intersection_pl(p, to_polygons(expolygon));
|
||||
}
|
||||
|
||||
// connect paths
|
||||
@ -122,7 +122,7 @@ void FillHoneycomb::_fill_surface_single(
|
||||
}
|
||||
|
||||
// clip paths again to prevent connection segments from crossing the expolygon boundaries
|
||||
intersection(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)), &paths);
|
||||
paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON)));
|
||||
// Move the polylines to the output, avoid a deep copy.
|
||||
size_t j = polylines_out.size();
|
||||
polylines_out.resize(j + paths.size(), Polyline());
|
||||
|
@ -44,7 +44,7 @@ void FillPlanePath::_fill_surface_single(
|
||||
coord_t(floor(it->x * distance_between_lines + 0.5)),
|
||||
coord_t(floor(it->y * distance_between_lines + 0.5))));
|
||||
// intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
|
||||
intersection(polylines, (Polygons)expolygon, &polylines);
|
||||
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
||||
|
||||
/*
|
||||
if (1) {
|
||||
|
@ -63,7 +63,7 @@ void FillRectilinear::_fill_surface_single(
|
||||
pts.push_back(it->a);
|
||||
pts.push_back(it->b);
|
||||
}
|
||||
Polylines polylines = intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), false);
|
||||
Polylines polylines = intersection_pl(polylines_src, offset(to_polygons(expolygon), scale_(0.02)), false);
|
||||
|
||||
// FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
|
||||
const float INFILL_OVERLAP_OVER_SPACING = 0.3f;
|
||||
|
@ -372,11 +372,9 @@ public:
|
||||
bool sticks_removed = remove_sticks(polygons_src);
|
||||
// if (sticks_removed) printf("Sticks removed!\n");
|
||||
polygons_outer = offset(polygons_src, aoffset1,
|
||||
CLIPPER_OFFSET_SCALE,
|
||||
ClipperLib::jtMiter,
|
||||
mitterLimit);
|
||||
polygons_inner = offset(polygons_outer, aoffset2 - aoffset1,
|
||||
CLIPPER_OFFSET_SCALE,
|
||||
ClipperLib::jtMiter,
|
||||
mitterLimit);
|
||||
// Filter out contours with zero area or small area, contours with 2 points only.
|
||||
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {};
|
||||
|
@ -315,8 +315,7 @@ GCode::change_layer(const Layer &layer)
|
||||
|
||||
// avoid computing islands and overhangs if they're not needed
|
||||
if (this->config.avoid_crossing_perimeters) {
|
||||
ExPolygons islands;
|
||||
union_(layer.slices, &islands, true);
|
||||
ExPolygons islands = union_ex(layer.slices, true);
|
||||
this->avoid_crossing_perimeters.init_layer_mp(islands);
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ Layer::make_slices()
|
||||
FOREACH_LAYERREGION(this, layerm) {
|
||||
polygons_append(slices_p, to_polygons((*layerm)->slices));
|
||||
}
|
||||
union_(slices_p, &slices);
|
||||
slices = union_ex(slices_p);
|
||||
}
|
||||
|
||||
this->slices.expolygons.clear();
|
||||
@ -132,15 +132,11 @@ Layer::merge_slices()
|
||||
if (this->regions.size() == 1) {
|
||||
// Optimization, also more robust. Don't merge classified pieces of layerm->slices,
|
||||
// but use the non-split islands of a layer. For a single region print, these shall be equal.
|
||||
this->regions.front()->slices.surfaces.clear();
|
||||
surfaces_append(this->regions.front()->slices.surfaces, this->slices.expolygons, stInternal);
|
||||
this->regions.front()->slices.set(this->slices.expolygons, stInternal);
|
||||
} else {
|
||||
FOREACH_LAYERREGION(this, layerm) {
|
||||
ExPolygons expp;
|
||||
// without safety offset, artifacts are generated (GH #2494)
|
||||
union_(to_polygons(STDMOVE((*layerm)->slices.surfaces)), &expp, true);
|
||||
(*layerm)->slices.surfaces.clear();
|
||||
surfaces_append((*layerm)->slices.surfaces, expp, stInternal);
|
||||
(*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -223,7 +219,7 @@ Layer::make_perimeters()
|
||||
}
|
||||
// merge the surfaces assigned to each group
|
||||
for (std::map<unsigned short,Surfaces>::const_iterator it = slices.begin(); it != slices.end(); ++it)
|
||||
surfaces_append(new_slices.surfaces, union_ex(it->second, true), it->second.front());
|
||||
new_slices.append(union_ex(it->second, true), it->second.front());
|
||||
}
|
||||
|
||||
// make perimeters
|
||||
@ -236,8 +232,7 @@ Layer::make_perimeters()
|
||||
// Separate the fill surfaces.
|
||||
ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices);
|
||||
(*l)->fill_expolygons = expp;
|
||||
(*l)->fill_surfaces.surfaces.clear();
|
||||
surfaces_append((*l)->fill_surfaces.surfaces, STDMOVE(expp), fill_surfaces.surfaces.front());
|
||||
(*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -318,9 +313,4 @@ SupportLayer::SupportLayer(size_t id, PrintObject *object, coordf_t height,
|
||||
{
|
||||
}
|
||||
|
||||
SupportLayer::~SupportLayer()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -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() {}
|
||||
};
|
||||
|
||||
|
||||
|
@ -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));
|
||||
|
@ -76,20 +76,20 @@ class Linef3
|
||||
void scale(double factor);
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Slic3r
|
||||
|
||||
// start Boost
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
template <>
|
||||
struct geometry_concept<Line> { typedef segment_concept type; };
|
||||
struct geometry_concept<Slic3r::Line> { typedef segment_concept type; };
|
||||
|
||||
template <>
|
||||
struct segment_traits<Line> {
|
||||
struct segment_traits<Slic3r::Line> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef Point point_type;
|
||||
typedef Slic3r::Point point_type;
|
||||
|
||||
static inline point_type get(const Line& line, direction_1d dir) {
|
||||
static inline point_type get(const Slic3r::Line& line, direction_1d dir) {
|
||||
return dir.to_int() ? line.b : line.a;
|
||||
}
|
||||
};
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "Layer.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "Slicing.hpp"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
@ -142,7 +142,7 @@ MotionPlanner::shortest_path(const Point &from, const Point &to)
|
||||
{
|
||||
// grow our environment slightly in order for simplify_by_visibility()
|
||||
// to work best by considering moves on boundaries valid as well
|
||||
ExPolygonCollection grown_env(offset_ex(env.env, +SCALED_EPSILON));
|
||||
ExPolygonCollection grown_env(offset_ex(env.env.expolygons, +SCALED_EPSILON));
|
||||
|
||||
if (island_idx == -1) {
|
||||
/* If 'from' or 'to' are not inside our env, they were connected using the
|
||||
@ -155,12 +155,12 @@ MotionPlanner::shortest_path(const Point &from, const Point &to)
|
||||
if (!grown_env.contains(from)) {
|
||||
// delete second point while the line connecting first to third crosses the
|
||||
// boundaries as many times as the current first to second
|
||||
while (polyline.points.size() > 2 && intersection((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) {
|
||||
while (polyline.points.size() > 2 && intersection_ln((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) {
|
||||
polyline.points.erase(polyline.points.begin() + 1);
|
||||
}
|
||||
}
|
||||
if (!grown_env.contains(to)) {
|
||||
while (polyline.points.size() > 2 && intersection((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) {
|
||||
while (polyline.points.size() > 2 && intersection_ln((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) {
|
||||
polyline.points.erase(polyline.points.end() - 2);
|
||||
}
|
||||
}
|
||||
@ -294,7 +294,7 @@ MotionPlannerEnv::nearest_env_point(const Point &from, const Point &to) const
|
||||
size_t result = from.nearest_waypoint_index(pp, to);
|
||||
|
||||
// as we assume 'from' is outside env, any node will require at least one crossing
|
||||
if (intersection((Lines)Line(from, pp[result]), this->island).size() > 1) {
|
||||
if (intersection_ln((Lines)Line(from, pp[result]), this->island).size() > 1) {
|
||||
// discard result
|
||||
pp.erase(pp.begin() + result);
|
||||
} else {
|
||||
|
@ -54,8 +54,7 @@ PerimeterGenerator::process()
|
||||
for (Surfaces::const_iterator surface = this->slices->surfaces.begin();
|
||||
surface != this->slices->surfaces.end(); ++surface) {
|
||||
// detect how many perimeters must be generated for this island
|
||||
signed short loop_number = this->config->perimeters + surface->extra_perimeters;
|
||||
loop_number--; // 0-indexed loops
|
||||
const int loop_number = this->config->perimeters + surface->extra_perimeters -1; // 0-indexed loops
|
||||
|
||||
Polygons gaps;
|
||||
|
||||
@ -67,7 +66,7 @@ PerimeterGenerator::process()
|
||||
ThickPolylines thin_walls;
|
||||
|
||||
// we loop one time more than needed in order to find gaps after the last perimeter was applied
|
||||
for (signed short i = 0; i <= loop_number+1; ++i) { // outer loop is 0
|
||||
for (int i = 0; i <= loop_number+1; ++i) { // outer loop is 0
|
||||
Polygons offsets;
|
||||
if (i == 0) {
|
||||
// the minimum thickness of a single loop is:
|
||||
@ -170,16 +169,16 @@ PerimeterGenerator::process()
|
||||
}
|
||||
|
||||
// nest loops: holes first
|
||||
for (signed short d = 0; d <= loop_number; ++d) {
|
||||
for (int d = 0; d <= loop_number; ++d) {
|
||||
PerimeterGeneratorLoops &holes_d = holes[d];
|
||||
|
||||
// loop through all holes having depth == d
|
||||
for (signed short i = 0; i < holes_d.size(); ++i) {
|
||||
for (int i = 0; i < (int)holes_d.size(); ++i) {
|
||||
const PerimeterGeneratorLoop &loop = holes_d[i];
|
||||
|
||||
// find the hole loop that contains this one, if any
|
||||
for (signed short t = d+1; t <= loop_number; ++t) {
|
||||
for (signed short j = 0; j < holes[t].size(); ++j) {
|
||||
for (int t = d+1; t <= loop_number; ++t) {
|
||||
for (int j = 0; j < (int)holes[t].size(); ++j) {
|
||||
PerimeterGeneratorLoop &candidate_parent = holes[t][j];
|
||||
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
|
||||
candidate_parent.children.push_back(loop);
|
||||
@ -191,8 +190,8 @@ PerimeterGenerator::process()
|
||||
}
|
||||
|
||||
// if no hole contains this hole, find the contour loop that contains it
|
||||
for (signed short t = loop_number; t >= 0; --t) {
|
||||
for (signed short j = 0; j < contours[t].size(); ++j) {
|
||||
for (int t = loop_number; t >= 0; --t) {
|
||||
for (int j = 0; j < (int)contours[t].size(); ++j) {
|
||||
PerimeterGeneratorLoop &candidate_parent = contours[t][j];
|
||||
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
|
||||
candidate_parent.children.push_back(loop);
|
||||
@ -207,16 +206,16 @@ PerimeterGenerator::process()
|
||||
}
|
||||
|
||||
// nest contour loops
|
||||
for (signed short d = loop_number; d >= 1; --d) {
|
||||
for (int d = loop_number; d >= 1; --d) {
|
||||
PerimeterGeneratorLoops &contours_d = contours[d];
|
||||
|
||||
// loop through all contours having depth == d
|
||||
for (signed short i = 0; i < contours_d.size(); ++i) {
|
||||
for (int i = 0; i < (int)contours_d.size(); ++i) {
|
||||
const PerimeterGeneratorLoop &loop = contours_d[i];
|
||||
|
||||
// find the contour loop that contains it
|
||||
for (signed short t = d-1; t >= 0; --t) {
|
||||
for (signed short j = 0; j < contours[t].size(); ++j) {
|
||||
for (int t = d-1; t >= 0; --t) {
|
||||
for (int j = 0; j < contours[t].size(); ++j) {
|
||||
PerimeterGeneratorLoop &candidate_parent = contours[t][j];
|
||||
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
|
||||
candidate_parent.children.push_back(loop);
|
||||
@ -315,8 +314,7 @@ PerimeterGenerator::process()
|
||||
coord_t min_perimeter_infill_spacing = ispacing * (1 - INSET_OVERLAP_TOLERANCE);
|
||||
|
||||
// append infill areas to fill_surfaces
|
||||
surfaces_append(
|
||||
this->fill_surfaces->surfaces,
|
||||
this->fill_surfaces->append(
|
||||
offset2_ex(
|
||||
pp,
|
||||
-inset -min_perimeter_infill_spacing/2,
|
||||
@ -354,36 +352,24 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,
|
||||
if (this->config->overhangs && this->layer_id > 0
|
||||
&& !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) {
|
||||
// get non-overhang paths by intersecting this loop with the grown lower slices
|
||||
{
|
||||
Polylines polylines;
|
||||
intersection((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
|
||||
|
||||
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
|
||||
ExtrusionPath path(role);
|
||||
path.polyline = *polyline;
|
||||
path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm;
|
||||
path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width;
|
||||
path.height = this->layer_height;
|
||||
paths.push_back(path);
|
||||
}
|
||||
}
|
||||
extrusion_paths_append(
|
||||
paths,
|
||||
intersection_pl(loop->polygon, this->_lower_slices_p),
|
||||
role,
|
||||
is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm,
|
||||
is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width,
|
||||
this->layer_height);
|
||||
|
||||
// get overhang paths by checking what parts of this loop fall
|
||||
// outside the grown lower slices (thus where the distance between
|
||||
// the loop centerline and original lower slices is >= half nozzle diameter
|
||||
{
|
||||
Polylines polylines;
|
||||
diff((Polygons)loop->polygon, this->_lower_slices_p, &polylines);
|
||||
|
||||
for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) {
|
||||
ExtrusionPath path(erOverhangPerimeter);
|
||||
path.polyline = *polyline;
|
||||
path.mm3_per_mm = this->_mm3_per_mm_overhang;
|
||||
path.width = this->overhang_flow.width;
|
||||
path.height = this->overhang_flow.height;
|
||||
paths.push_back(path);
|
||||
}
|
||||
}
|
||||
extrusion_paths_append(
|
||||
paths,
|
||||
diff_pl(loop->polygon, this->_lower_slices_p),
|
||||
erOverhangPerimeter,
|
||||
this->_mm3_per_mm_overhang,
|
||||
this->overhang_flow.width,
|
||||
this->overhang_flow.height);
|
||||
|
||||
// reapply the nearest point search for starting point
|
||||
// We allow polyline reversal because Clipper may have randomly
|
||||
@ -459,7 +445,7 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo
|
||||
ExtrusionPath path(role);
|
||||
ThickLines lines = p->thicklines();
|
||||
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
for (int i = 0; i < (int)lines.size(); ++i) {
|
||||
const ThickLine& line = lines[i];
|
||||
|
||||
const coordf_t line_len = line.length();
|
||||
|
@ -12,12 +12,6 @@ Point::Point(double x, double y)
|
||||
this->y = lrint(y);
|
||||
}
|
||||
|
||||
bool
|
||||
Point::operator==(const Point& rhs) const
|
||||
{
|
||||
return this->coincides_with(rhs);
|
||||
}
|
||||
|
||||
std::string
|
||||
Point::wkt() const
|
||||
{
|
||||
|
@ -36,7 +36,7 @@ class Point
|
||||
static Point new_scale(coordf_t x, coordf_t y) {
|
||||
return Point(scale_(x), scale_(y));
|
||||
};
|
||||
bool operator==(const Point& rhs) const;
|
||||
bool operator==(const Point& rhs) const { return this->x == rhs.x && this->y == rhs.y; }
|
||||
std::string wkt() const;
|
||||
std::string dump_perl() const;
|
||||
void scale(double factor);
|
||||
@ -70,6 +70,12 @@ inline Point operator+(const Point& point1, const Point& point2) { return Point(
|
||||
inline Point operator-(const Point& point1, const Point& point2) { return Point(point1.x - point2.x, point1.y - point2.y); }
|
||||
inline Point operator*(double scalar, const Point& point2) { return Point(scalar * point2.x, scalar * point2.y); }
|
||||
|
||||
struct PointHash {
|
||||
size_t operator()(const Point &pt) const {
|
||||
return std::hash<coord_t>()(pt.x) ^ std::hash<coord_t>()(pt.y);
|
||||
}
|
||||
};
|
||||
|
||||
class Point3 : public Point
|
||||
{
|
||||
public:
|
||||
@ -105,6 +111,9 @@ class Pointf
|
||||
inline Pointf operator+(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x + point2.x, point1.y + point2.y); }
|
||||
inline Pointf operator-(const Pointf& point1, const Pointf& point2) { return Pointf(point1.x - point2.x, point1.y - point2.y); }
|
||||
inline Pointf operator*(double scalar, const Pointf& point2) { return Pointf(scalar * point2.x, scalar * point2.y); }
|
||||
inline Pointf operator*(const Pointf& point2, double scalar) { return Pointf(scalar * point2.x, scalar * point2.y); }
|
||||
inline coordf_t cross(const Pointf &v1, const Pointf &v2) { return v1.x * v2.y - v1.y * v2.x; }
|
||||
inline coordf_t dot(const Pointf &v1, const Pointf &v2) { return v1.x * v1.y + v2.x * v2.y; }
|
||||
|
||||
class Pointf3 : public Pointf
|
||||
{
|
||||
@ -122,7 +131,7 @@ class Pointf3 : public Pointf
|
||||
Vectorf3 vector_to(const Pointf3 &point) const;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Slic3r
|
||||
|
||||
// start Boost
|
||||
#include <boost/version.hpp>
|
||||
@ -146,28 +155,28 @@ namespace boost { namespace polygon {
|
||||
#endif
|
||||
|
||||
template <>
|
||||
struct geometry_concept<Point> { typedef point_concept type; };
|
||||
struct geometry_concept<Slic3r::Point> { typedef point_concept type; };
|
||||
|
||||
template <>
|
||||
struct point_traits<Point> {
|
||||
struct point_traits<Slic3r::Point> {
|
||||
typedef coord_t coordinate_type;
|
||||
|
||||
static inline coordinate_type get(const Point& point, orientation_2d orient) {
|
||||
static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) {
|
||||
return (orient == HORIZONTAL) ? point.x : point.y;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct point_mutable_traits<Point> {
|
||||
struct point_mutable_traits<Slic3r::Point> {
|
||||
typedef coord_t coordinate_type;
|
||||
static inline void set(Point& point, orientation_2d orient, coord_t value) {
|
||||
static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) {
|
||||
if (orient == HORIZONTAL)
|
||||
point.x = value;
|
||||
else
|
||||
point.y = value;
|
||||
}
|
||||
static inline Point construct(coord_t x_value, coord_t y_value) {
|
||||
Point retval;
|
||||
static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) {
|
||||
Slic3r::Point retval;
|
||||
retval.x = x_value;
|
||||
retval.y = y_value;
|
||||
return retval;
|
||||
|
@ -112,9 +112,7 @@ double Polygon::area() const
|
||||
bool
|
||||
Polygon::is_counter_clockwise() const
|
||||
{
|
||||
ClipperLib::Path p;
|
||||
Slic3rMultiPoint_to_ClipperPath(*this, &p);
|
||||
return ClipperLib::Orientation(p);
|
||||
return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -190,8 +188,7 @@ Polygon::simplify(double tolerance) const
|
||||
|
||||
Polygons pp;
|
||||
pp.push_back(p);
|
||||
simplify_polygons(pp, &pp);
|
||||
return pp;
|
||||
return simplify_polygons(pp);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -142,43 +142,43 @@ inline Polylines to_polylines(Polygons &&polys)
|
||||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
template <>
|
||||
struct geometry_concept<Polygon>{ typedef polygon_concept type; };
|
||||
struct geometry_concept<Slic3r::Polygon>{ typedef polygon_concept type; };
|
||||
|
||||
template <>
|
||||
struct polygon_traits<Polygon> {
|
||||
struct polygon_traits<Slic3r::Polygon> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef Points::const_iterator iterator_type;
|
||||
typedef Point point_type;
|
||||
typedef Slic3r::Points::const_iterator iterator_type;
|
||||
typedef Slic3r::Point point_type;
|
||||
|
||||
// Get the begin iterator
|
||||
static inline iterator_type begin_points(const Polygon& t) {
|
||||
static inline iterator_type begin_points(const Slic3r::Polygon& t) {
|
||||
return t.points.begin();
|
||||
}
|
||||
|
||||
// Get the end iterator
|
||||
static inline iterator_type end_points(const Polygon& t) {
|
||||
static inline iterator_type end_points(const Slic3r::Polygon& t) {
|
||||
return t.points.end();
|
||||
}
|
||||
|
||||
// Get the number of sides of the polygon
|
||||
static inline std::size_t size(const Polygon& t) {
|
||||
static inline std::size_t size(const Slic3r::Polygon& t) {
|
||||
return t.points.size();
|
||||
}
|
||||
|
||||
// Get the winding direction of the polygon
|
||||
static inline winding_direction winding(const Polygon& t) {
|
||||
static inline winding_direction winding(const Slic3r::Polygon& t) {
|
||||
return unknown_winding;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_mutable_traits<Polygon> {
|
||||
struct polygon_mutable_traits<Slic3r::Polygon> {
|
||||
// expects stl style iterators
|
||||
template <typename iT>
|
||||
static inline Polygon& set_points(Polygon& polygon, iT input_begin, iT input_end) {
|
||||
static inline Slic3r::Polygon& set_points(Slic3r::Polygon& polygon, iT input_begin, iT input_end) {
|
||||
polygon.points.clear();
|
||||
while (input_begin != input_end) {
|
||||
polygon.points.push_back(Point());
|
||||
polygon.points.push_back(Slic3r::Point());
|
||||
boost::polygon::assign(polygon.points.back(), *input_begin);
|
||||
++input_begin;
|
||||
}
|
||||
@ -189,32 +189,32 @@ namespace boost { namespace polygon {
|
||||
};
|
||||
|
||||
template <>
|
||||
struct geometry_concept<Polygons> { typedef polygon_set_concept type; };
|
||||
struct geometry_concept<Slic3r::Polygons> { typedef polygon_set_concept type; };
|
||||
|
||||
//next we map to the concept through traits
|
||||
template <>
|
||||
struct polygon_set_traits<Polygons> {
|
||||
struct polygon_set_traits<Slic3r::Polygons> {
|
||||
typedef coord_t coordinate_type;
|
||||
typedef Polygons::const_iterator iterator_type;
|
||||
typedef Polygons operator_arg_type;
|
||||
typedef Slic3r::Polygons::const_iterator iterator_type;
|
||||
typedef Slic3r::Polygons operator_arg_type;
|
||||
|
||||
static inline iterator_type begin(const Polygons& polygon_set) {
|
||||
static inline iterator_type begin(const Slic3r::Polygons& polygon_set) {
|
||||
return polygon_set.begin();
|
||||
}
|
||||
|
||||
static inline iterator_type end(const Polygons& polygon_set) {
|
||||
static inline iterator_type end(const Slic3r::Polygons& polygon_set) {
|
||||
return polygon_set.end();
|
||||
}
|
||||
|
||||
//don't worry about these, just return false from them
|
||||
static inline bool clean(const Polygons& polygon_set) { return false; }
|
||||
static inline bool sorted(const Polygons& polygon_set) { return false; }
|
||||
static inline bool clean(const Slic3r::Polygons& polygon_set) { return false; }
|
||||
static inline bool sorted(const Slic3r::Polygons& polygon_set) { return false; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct polygon_set_mutable_traits<Polygons> {
|
||||
struct polygon_set_mutable_traits<Slic3r::Polygons> {
|
||||
template <typename input_iterator_type>
|
||||
static inline void set(Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) {
|
||||
static inline void set(Slic3r::Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) {
|
||||
polygons.assign(input_begin, input_end);
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "Layer.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "PlaceholderParser.hpp"
|
||||
|
||||
#include "Slicing.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -78,6 +78,10 @@ public:
|
||||
std::map< size_t,std::vector<int> > region_volumes;
|
||||
PrintObjectConfig config;
|
||||
t_layer_height_ranges layer_height_ranges;
|
||||
|
||||
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
|
||||
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
|
||||
std::vector<coordf_t> layer_height_profile;
|
||||
|
||||
// this is set to true when LayerRegion->slices is split in top/internal/bottom
|
||||
// so that next call to make_perimeters() performs a union() before computing loops
|
||||
@ -136,13 +140,27 @@ public:
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
bool invalidate_step(PrintObjectStep step);
|
||||
bool invalidate_all_steps();
|
||||
|
||||
|
||||
// Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile.
|
||||
// The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces.
|
||||
void update_layer_height_profile();
|
||||
|
||||
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
|
||||
// by the interactive layer height editor and by the printing process itself.
|
||||
// The slicing parameters are dependent on various configuration values
|
||||
// (layer height, first layer height, raft settings, print nozzle diameter etc).
|
||||
SlicingParameters slicing_parameters() const;
|
||||
|
||||
void _slice();
|
||||
bool has_support_material() const;
|
||||
void detect_surfaces_type();
|
||||
void process_external_surfaces();
|
||||
void discover_vertical_shells();
|
||||
void bridge_over_infill();
|
||||
|
||||
void _make_perimeters();
|
||||
void _infill();
|
||||
void _generate_support_material();
|
||||
|
||||
private:
|
||||
Print* _print;
|
||||
ModelObject* _model_object;
|
||||
@ -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
|
||||
|
@ -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,9 +1316,11 @@ PrintConfigDef::PrintConfigDef()
|
||||
def->cli = "threads|j=i";
|
||||
def->readonly = true;
|
||||
def->min = 1;
|
||||
def->max = 16;
|
||||
def->default_value = new ConfigOptionInt(2);
|
||||
|
||||
{
|
||||
unsigned int threads = boost::thread::hardware_concurrency();
|
||||
def->default_value = new ConfigOptionInt(threads > 0 ? threads : 2);
|
||||
}
|
||||
|
||||
def = this->add("toolchange_gcode", coString);
|
||||
def->label = "Tool change G-code";
|
||||
def->tooltip = "This custom code is inserted right before every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder].";
|
||||
|
@ -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);
|
||||
|
@ -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 ®ion = **region_it;
|
||||
|
||||
|
||||
if (!region.config.extra_perimeters
|
||||
|| region.config.perimeters == 0
|
||||
|| region.config.fill_density == 0
|
||||
|| this->layer_count() < 2) continue;
|
||||
|
||||
for (int i = 0; i < int(this->layer_count()) - 1; ++i) {
|
||||
LayerRegion &layerm = *this->get_layer(i)->get_region(region_id);
|
||||
const LayerRegion &upper_layerm = *this->get_layer(i+1)->get_region(region_id);
|
||||
const Polygons upper_layerm_polygons = upper_layerm.slices;
|
||||
|
||||
// Filter upper layer polygons in intersection_ppl by their bounding boxes?
|
||||
// my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
|
||||
double total_loop_length = 0;
|
||||
for (Polygons::const_iterator it = upper_layerm_polygons.begin(); it != upper_layerm_polygons.end(); ++it)
|
||||
total_loop_length += it->length();
|
||||
|
||||
const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing();
|
||||
const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter);
|
||||
const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width();
|
||||
const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing();
|
||||
|
||||
for (Surfaces::iterator slice = layerm.slices.surfaces.begin();
|
||||
slice != layerm.slices.surfaces.end(); ++slice) {
|
||||
while (true) {
|
||||
// compute the total thickness of perimeters
|
||||
const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
|
||||
+ (region.config.perimeters-1 + region.config.extra_perimeters) * perimeter_spacing;
|
||||
|
||||
// define a critical area where we don't want the upper slice to fall into
|
||||
// (it should either lay over our perimeters or outside this area)
|
||||
const coord_t critical_area_depth = perimeter_spacing * 1.5;
|
||||
const Polygons critical_area = diff(
|
||||
offset(slice->expolygon, -perimeters_thickness),
|
||||
offset(slice->expolygon, -(perimeters_thickness + critical_area_depth))
|
||||
);
|
||||
|
||||
// check whether a portion of the upper slices falls inside the critical area
|
||||
const Polylines intersection = intersection_pl(
|
||||
to_polylines(upper_layerm_polygons),
|
||||
critical_area
|
||||
);
|
||||
|
||||
// only add an additional loop if at least 30% of the slice loop would benefit from it
|
||||
{
|
||||
double total_intersection_length = 0;
|
||||
for (Polylines::const_iterator it = intersection.begin(); it != intersection.end(); ++it)
|
||||
total_intersection_length += it->length();
|
||||
if (total_intersection_length <= total_loop_length*0.3) break;
|
||||
}
|
||||
|
||||
/*
|
||||
if (0) {
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output(
|
||||
"extra.svg",
|
||||
no_arrows => 1,
|
||||
expolygons => union_ex($critical_area),
|
||||
polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
slice->extra_perimeters++;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if (slice->extra_perimeters > 0)
|
||||
printf(" adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, i);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parallelize<Layer*>(
|
||||
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
|
||||
boost::bind(&Slic3r::Layer::make_perimeters, _1),
|
||||
this->_print->config.threads.value
|
||||
);
|
||||
|
||||
/*
|
||||
simplify slices (both layer and region slices),
|
||||
we only need the max resolution for perimeters
|
||||
### This makes this method not-idempotent, so we keep it disabled for now.
|
||||
###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
|
||||
*/
|
||||
|
||||
this->state.set_done(posPerimeters);
|
||||
}
|
||||
|
||||
void
|
||||
PrintObject::_infill()
|
||||
{
|
||||
if (this->state.is_done(posInfill)) return;
|
||||
this->state.set_started(posInfill);
|
||||
|
||||
parallelize<Layer*>(
|
||||
std::queue<Layer*>(std::deque<Layer*>(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue<Layer*>
|
||||
boost::bind(&Slic3r::Layer::make_fills, _1),
|
||||
this->_print->config.threads.value
|
||||
);
|
||||
|
||||
/* we could free memory now, but this would make this step not idempotent
|
||||
### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
|
||||
*/
|
||||
|
||||
this->state.set_done(posInfill);
|
||||
}
|
||||
|
||||
void PrintObject::_generate_support_material()
|
||||
{
|
||||
PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters());
|
||||
support_material.generate(*this);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -12,7 +12,7 @@ namespace Slic3r {
|
||||
|
||||
class SVG
|
||||
{
|
||||
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); }
|
||||
};
|
||||
|
||||
}
|
||||
|
631
xs/src/libslic3r/Slicing.cpp
Normal file
631
xs/src/libslic3r/Slicing.cpp
Normal file
@ -0,0 +1,631 @@
|
||||
#include "Slicing.hpp"
|
||||
#include "SlicingAdaptive.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Model.hpp"
|
||||
|
||||
// #define SLIC3R_DEBUG
|
||||
|
||||
// Make assert active if SLIC3R_DEBUG
|
||||
#ifdef SLIC3R_DEBUG
|
||||
#undef NDEBUG
|
||||
#define DEBUG
|
||||
#define _DEBUG
|
||||
#include "SVG.hpp"
|
||||
#undef assert
|
||||
#include <cassert>
|
||||
#endif
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
SlicingParameters SlicingParameters::create_from_config(
|
||||
const PrintConfig &print_config,
|
||||
const PrintObjectConfig &object_config,
|
||||
coordf_t object_height,
|
||||
const std::set<size_t> &object_extruders)
|
||||
{
|
||||
coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ?
|
||||
object_config.layer_height.value :
|
||||
object_config.first_layer_height.get_abs_value(object_config.layer_height.value);
|
||||
coordf_t support_material_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_extruder.value - 1);
|
||||
coordf_t support_material_interface_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_interface_extruder.value - 1);
|
||||
bool soluble_interface = object_config.support_material_contact_distance.value == 0.;
|
||||
|
||||
SlicingParameters params;
|
||||
params.layer_height = object_config.layer_height.value;
|
||||
params.first_print_layer_height = first_layer_height;
|
||||
params.first_object_layer_height = first_layer_height;
|
||||
params.object_print_z_min = 0.;
|
||||
params.object_print_z_max = object_height;
|
||||
params.base_raft_layers = object_config.raft_layers.value;
|
||||
params.soluble_interface = soluble_interface;
|
||||
|
||||
if (! soluble_interface) {
|
||||
params.gap_raft_object = object_config.support_material_contact_distance.value;
|
||||
params.gap_object_support = object_config.support_material_contact_distance.value;
|
||||
params.gap_support_object = object_config.support_material_contact_distance.value;
|
||||
}
|
||||
|
||||
if (params.base_raft_layers > 0) {
|
||||
params.interface_raft_layers = (params.base_raft_layers + 1) / 2;
|
||||
params.base_raft_layers -= params.interface_raft_layers;
|
||||
// Use as large as possible layer height for the intermediate raft layers.
|
||||
params.base_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_extruder_dmr);
|
||||
params.interface_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr);
|
||||
params.contact_raft_layer_height_bridging = false;
|
||||
params.first_object_layer_bridging = false;
|
||||
#if 1
|
||||
params.contact_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr);
|
||||
if (! soluble_interface) {
|
||||
// Compute the average of all nozzles used for printing the object over a raft.
|
||||
//FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa?
|
||||
coordf_t average_object_extruder_dmr = 0.;
|
||||
if (! object_extruders.empty()) {
|
||||
for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder)
|
||||
average_object_extruder_dmr += print_config.nozzle_diameter.get_at(*it_extruder);
|
||||
average_object_extruder_dmr /= coordf_t(object_extruders.size());
|
||||
}
|
||||
params.first_object_layer_height = average_object_extruder_dmr;
|
||||
params.first_object_layer_bridging = true;
|
||||
}
|
||||
#else
|
||||
params.contact_raft_layer_height = soluble_interface ? support_material_interface_extruder_dmr : 0.75 * support_material_interface_extruder_dmr;
|
||||
params.contact_raft_layer_height_bridging = ! soluble_interface;
|
||||
...
|
||||
#endif
|
||||
}
|
||||
|
||||
if (params.has_raft()) {
|
||||
// Raise first object layer Z by the thickness of the raft itself plus the extra distance required by the support material logic.
|
||||
//FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case.
|
||||
if (params.raft_layers() == 1) {
|
||||
// There is only the contact layer.
|
||||
params.contact_raft_layer_height = first_layer_height;
|
||||
params.raft_contact_top_z = first_layer_height;
|
||||
} else {
|
||||
assert(params.base_raft_layers > 0);
|
||||
assert(params.interface_raft_layers > 0);
|
||||
// Number of the base raft layers is decreased by the first layer.
|
||||
params.raft_base_top_z = first_layer_height + coordf_t(params.base_raft_layers - 1) * params.base_raft_layer_height;
|
||||
// Number of the interface raft layers is decreased by the contact layer.
|
||||
params.raft_interface_top_z = params.raft_base_top_z + coordf_t(params.interface_raft_layers - 1) * params.interface_raft_layer_height;
|
||||
params.raft_contact_top_z = params.raft_interface_top_z + params.contact_raft_layer_height;
|
||||
}
|
||||
coordf_t print_z = params.raft_contact_top_z + params.gap_raft_object;
|
||||
params.object_print_z_min = print_z;
|
||||
params.object_print_z_max += print_z;
|
||||
}
|
||||
|
||||
params.min_layer_height = std::min(params.layer_height, first_layer_height);
|
||||
params.max_layer_height = std::max(params.layer_height, first_layer_height);
|
||||
|
||||
//FIXME add it to the print configuration
|
||||
params.min_layer_height = 0.05;
|
||||
|
||||
// Calculate the maximum layer height as 0.75 from the minimum nozzle diameter.
|
||||
if (! object_extruders.empty()) {
|
||||
coordf_t min_object_extruder_dmr = 1000000.;
|
||||
for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder)
|
||||
min_object_extruder_dmr = std::min(min_object_extruder_dmr, print_config.nozzle_diameter.get_at(*it_extruder));
|
||||
// Allow excessive maximum layer height higher than 0.75 * min_object_extruder_dmr
|
||||
params.max_layer_height = std::max(std::max(params.layer_height, first_layer_height), 0.75 * min_object_extruder_dmr);
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
// Convert layer_height_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
|
||||
// in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation.
|
||||
std::vector<coordf_t> layer_height_profile_from_ranges(
|
||||
const SlicingParameters &slicing_params,
|
||||
const t_layer_height_ranges &layer_height_ranges)
|
||||
{
|
||||
// 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed.
|
||||
std::vector<std::pair<t_layer_height_range,coordf_t>> ranges_non_overlapping;
|
||||
ranges_non_overlapping.reserve(layer_height_ranges.size() * 4);
|
||||
if (slicing_params.first_object_layer_height_fixed())
|
||||
ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(
|
||||
t_layer_height_range(0., slicing_params.first_object_layer_height),
|
||||
slicing_params.first_object_layer_height));
|
||||
// The height ranges are sorted lexicographically by low / high layer boundaries.
|
||||
for (t_layer_height_ranges::const_iterator it_range = layer_height_ranges.begin(); it_range != layer_height_ranges.end(); ++ it_range) {
|
||||
coordf_t lo = it_range->first.first;
|
||||
coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height());
|
||||
coordf_t height = it_range->second;
|
||||
if (! ranges_non_overlapping.empty())
|
||||
// Trim current low with the last high.
|
||||
lo = std::max(lo, ranges_non_overlapping.back().first.second);
|
||||
if (lo + EPSILON < hi)
|
||||
// Ignore too narrow ranges.
|
||||
ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(t_layer_height_range(lo, hi), height));
|
||||
}
|
||||
|
||||
// 2) Convert the trimmed ranges to a height profile, fill in the undefined intervals between z=0 and z=slicing_params.object_print_z_max()
|
||||
// with slicing_params.layer_height
|
||||
std::vector<coordf_t> layer_height_profile;
|
||||
for (std::vector<std::pair<t_layer_height_range,coordf_t>>::const_iterator it_range = ranges_non_overlapping.begin(); it_range != ranges_non_overlapping.end(); ++ it_range) {
|
||||
coordf_t lo = it_range->first.first;
|
||||
coordf_t hi = it_range->first.second;
|
||||
coordf_t height = it_range->second;
|
||||
coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
|
||||
coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
|
||||
if (lo > last_z + EPSILON) {
|
||||
// Insert a step of normal layer height.
|
||||
layer_height_profile.push_back(last_z);
|
||||
layer_height_profile.push_back(slicing_params.layer_height);
|
||||
layer_height_profile.push_back(lo);
|
||||
layer_height_profile.push_back(slicing_params.layer_height);
|
||||
}
|
||||
// Insert a step of the overriden layer height.
|
||||
layer_height_profile.push_back(lo);
|
||||
layer_height_profile.push_back(height);
|
||||
layer_height_profile.push_back(hi);
|
||||
layer_height_profile.push_back(height);
|
||||
}
|
||||
|
||||
coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
|
||||
coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
|
||||
if (last_z < slicing_params.object_print_z_height()) {
|
||||
// Insert a step of normal layer height up to the object top.
|
||||
layer_height_profile.push_back(last_z);
|
||||
layer_height_profile.push_back(slicing_params.layer_height);
|
||||
layer_height_profile.push_back(slicing_params.object_print_z_height());
|
||||
layer_height_profile.push_back(slicing_params.layer_height);
|
||||
}
|
||||
|
||||
return layer_height_profile;
|
||||
}
|
||||
|
||||
// Based on the work of @platsch
|
||||
// Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
|
||||
std::vector<coordf_t> layer_height_profile_adaptive(
|
||||
const SlicingParameters &slicing_params,
|
||||
const t_layer_height_ranges &layer_height_ranges,
|
||||
const ModelVolumePtrs &volumes)
|
||||
{
|
||||
// 1) Initialize the SlicingAdaptive class with the object meshes.
|
||||
SlicingAdaptive as;
|
||||
as.set_slicing_parameters(slicing_params);
|
||||
for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it)
|
||||
if (! (*it)->modifier)
|
||||
as.add_mesh(&(*it)->mesh);
|
||||
as.prepare();
|
||||
|
||||
// 2) Generate layers using the algorithm of @platsch
|
||||
// loop until we have at least one layer and the max slice_z reaches the object height
|
||||
//FIXME make it configurable
|
||||
// Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm.
|
||||
const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value');
|
||||
|
||||
std::vector<coordf_t> layer_height_profile;
|
||||
layer_height_profile.push_back(0.);
|
||||
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
||||
if (slicing_params.first_object_layer_height_fixed()) {
|
||||
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
||||
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
||||
}
|
||||
coordf_t slice_z = slicing_params.first_object_layer_height;
|
||||
coordf_t height = slicing_params.first_object_layer_height;
|
||||
coordf_t cusp_height = 0.;
|
||||
int current_facet = 0;
|
||||
while ((slice_z - height) <= slicing_params.object_print_z_height()) {
|
||||
height = 999;
|
||||
// Slic3r::debugf "\n Slice layer: %d\n", $id;
|
||||
// determine next layer height
|
||||
coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet);
|
||||
// check for horizontal features and object size
|
||||
/*
|
||||
if($self->config->get_value('match_horizontal_surfaces')) {
|
||||
my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height);
|
||||
if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) {
|
||||
Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist;
|
||||
# can we shrink the current layer a bit?
|
||||
if($cusp_height-($min_height-$horizontal_dist) > $min_height) {
|
||||
# yes we can
|
||||
$cusp_height = $cusp_height-($min_height-$horizontal_dist);
|
||||
Slic3r::debugf "Shrink layer height to %f\n", $cusp_height;
|
||||
}else{
|
||||
# no, current layer would become too thin
|
||||
$cusp_height = $cusp_height+$horizontal_dist;
|
||||
Slic3r::debugf "Widen layer height to %f\n", $cusp_height;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
height = std::min(cusp_height, height);
|
||||
|
||||
// apply z-gradation
|
||||
/*
|
||||
my $gradation = $self->config->get_value('adaptive_slicing_z_gradation');
|
||||
if($gradation > 0) {
|
||||
$height = $height - unscale((scale($height)) % (scale($gradation)));
|
||||
}
|
||||
*/
|
||||
|
||||
// look for an applicable custom range
|
||||
/*
|
||||
if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) {
|
||||
$height = $range->[2];
|
||||
|
||||
# if user set custom height to zero we should just skip the range and resume slicing over it
|
||||
if ($height == 0) {
|
||||
$slice_z += $range->[1] - $range->[0];
|
||||
next;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
layer_height_profile.push_back(slice_z);
|
||||
layer_height_profile.push_back(height);
|
||||
slice_z += height;
|
||||
layer_height_profile.push_back(slice_z);
|
||||
layer_height_profile.push_back(height);
|
||||
}
|
||||
|
||||
coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]);
|
||||
layer_height_profile.push_back(last);
|
||||
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
||||
layer_height_profile.push_back(slicing_params.object_print_z_height());
|
||||
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
||||
|
||||
return layer_height_profile;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T clamp(const T low, const T high, const T value)
|
||||
{
|
||||
return std::max(low, std::min(high, value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T lerp(const T a, const T b, const T t)
|
||||
{
|
||||
assert(t >= T(-EPSILON) && t <= T(1.+EPSILON));
|
||||
return (1. - t) * a + t * b;
|
||||
}
|
||||
|
||||
void adjust_layer_height_profile(
|
||||
const SlicingParameters &slicing_params,
|
||||
std::vector<coordf_t> &layer_height_profile,
|
||||
coordf_t z,
|
||||
coordf_t layer_thickness_delta,
|
||||
coordf_t band_width,
|
||||
LayerHeightEditActionType action)
|
||||
{
|
||||
// Constrain the profile variability by the 1st layer height.
|
||||
std::pair<coordf_t, coordf_t> z_span_variable =
|
||||
std::pair<coordf_t, coordf_t>(
|
||||
slicing_params.first_object_layer_height_fixed() ? slicing_params.first_object_layer_height : 0.,
|
||||
slicing_params.object_print_z_height());
|
||||
if (z < z_span_variable.first || z > z_span_variable.second)
|
||||
return;
|
||||
|
||||
assert(layer_height_profile.size() >= 2);
|
||||
|
||||
// 1) Get the current layer thickness at z.
|
||||
coordf_t current_layer_height = slicing_params.layer_height;
|
||||
for (size_t i = 0; i < layer_height_profile.size(); i += 2) {
|
||||
if (i + 2 == layer_height_profile.size()) {
|
||||
current_layer_height = layer_height_profile[i + 1];
|
||||
break;
|
||||
} else if (layer_height_profile[i + 2] > z) {
|
||||
coordf_t z1 = layer_height_profile[i];
|
||||
coordf_t h1 = layer_height_profile[i + 1];
|
||||
coordf_t z2 = layer_height_profile[i + 2];
|
||||
coordf_t h2 = layer_height_profile[i + 3];
|
||||
current_layer_height = lerp(h1, h2, (z - z1) / (z2 - z1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Is it possible to apply the delta?
|
||||
switch (action) {
|
||||
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:
|
||||
layer_thickness_delta = - layer_thickness_delta;
|
||||
// fallthrough
|
||||
case LAYER_HEIGHT_EDIT_ACTION_INCREASE:
|
||||
if (layer_thickness_delta > 0) {
|
||||
if (current_layer_height >= slicing_params.max_layer_height - EPSILON)
|
||||
return;
|
||||
layer_thickness_delta = std::min(layer_thickness_delta, slicing_params.max_layer_height - current_layer_height);
|
||||
} else {
|
||||
if (current_layer_height <= slicing_params.min_layer_height + EPSILON)
|
||||
return;
|
||||
layer_thickness_delta = std::max(layer_thickness_delta, slicing_params.min_layer_height - current_layer_height);
|
||||
}
|
||||
break;
|
||||
case LAYER_HEIGHT_EDIT_ACTION_REDUCE:
|
||||
case LAYER_HEIGHT_EDIT_ACTION_SMOOTH:
|
||||
layer_thickness_delta = std::abs(layer_thickness_delta);
|
||||
layer_thickness_delta = std::min(layer_thickness_delta, std::abs(slicing_params.layer_height - current_layer_height));
|
||||
if (layer_thickness_delta < EPSILON)
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// 3) Densify the profile inside z +- band_width/2, remove duplicate Zs from the height profile inside the band.
|
||||
coordf_t lo = std::max(z_span_variable.first, z - 0.5 * band_width);
|
||||
coordf_t hi = std::min(z_span_variable.second, z + 0.5 * band_width);
|
||||
coordf_t z_step = 0.1;
|
||||
size_t i = 0;
|
||||
while (i < layer_height_profile.size() && layer_height_profile[i] < lo)
|
||||
i += 2;
|
||||
i -= 2;
|
||||
|
||||
std::vector<double> profile_new;
|
||||
profile_new.reserve(layer_height_profile.size());
|
||||
assert(i >= 0 && i + 1 < layer_height_profile.size());
|
||||
profile_new.insert(profile_new.end(), layer_height_profile.begin(), layer_height_profile.begin() + i + 2);
|
||||
coordf_t zz = lo;
|
||||
size_t i_resampled_start = profile_new.size();
|
||||
while (zz < hi) {
|
||||
size_t next = i + 2;
|
||||
coordf_t z1 = layer_height_profile[i];
|
||||
coordf_t h1 = layer_height_profile[i + 1];
|
||||
coordf_t height = h1;
|
||||
if (next < layer_height_profile.size()) {
|
||||
coordf_t z2 = layer_height_profile[next];
|
||||
coordf_t h2 = layer_height_profile[next + 1];
|
||||
height = lerp(h1, h2, (zz - z1) / (z2 - z1));
|
||||
}
|
||||
// Adjust height by layer_thickness_delta.
|
||||
coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.;
|
||||
coordf_t height_new = height;
|
||||
switch (action) {
|
||||
case LAYER_HEIGHT_EDIT_ACTION_INCREASE:
|
||||
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:
|
||||
height += weight * layer_thickness_delta;
|
||||
break;
|
||||
case LAYER_HEIGHT_EDIT_ACTION_REDUCE:
|
||||
{
|
||||
coordf_t delta = height - slicing_params.layer_height;
|
||||
coordf_t step = weight * layer_thickness_delta;
|
||||
step = (std::abs(delta) > step) ?
|
||||
(delta > 0) ? -step : step :
|
||||
-delta;
|
||||
height += step;
|
||||
break;
|
||||
}
|
||||
case LAYER_HEIGHT_EDIT_ACTION_SMOOTH:
|
||||
{
|
||||
// Don't modify the profile during resampling process, do it at the next step.
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
// Avoid entering a too short segment.
|
||||
if (profile_new[profile_new.size() - 2] + EPSILON < zz) {
|
||||
profile_new.push_back(zz);
|
||||
profile_new.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, height));
|
||||
}
|
||||
zz += z_step;
|
||||
i = next;
|
||||
while (i < layer_height_profile.size() && layer_height_profile[i] < zz)
|
||||
i += 2;
|
||||
i -= 2;
|
||||
}
|
||||
|
||||
i += 2;
|
||||
assert(i > 0);
|
||||
size_t i_resampled_end = profile_new.size();
|
||||
if (i < layer_height_profile.size()) {
|
||||
assert(zz >= layer_height_profile[i - 2]);
|
||||
assert(zz <= layer_height_profile[i]);
|
||||
// profile_new.push_back(zz);
|
||||
// profile_new.push_back(layer_height_profile[i + 1]);
|
||||
profile_new.insert(profile_new.end(), layer_height_profile.begin() + i, layer_height_profile.end());
|
||||
}
|
||||
layer_height_profile = std::move(profile_new);
|
||||
|
||||
if (action == LAYER_HEIGHT_EDIT_ACTION_SMOOTH) {
|
||||
size_t n_rounds = 6;
|
||||
for (size_t i_round = 0; i_round < n_rounds; ++ i_round) {
|
||||
profile_new = layer_height_profile;
|
||||
for (size_t i = i_resampled_start; i < i_resampled_end; i += 2) {
|
||||
coordf_t zz = profile_new[i];
|
||||
coordf_t t = std::abs(zz - z) < 0.5 * band_width ? (0.25 + 0.25 * cos(2. * M_PI * (zz - z) / band_width)) : 0.;
|
||||
assert(t >= 0. && t <= 0.5000001);
|
||||
if (i == 0)
|
||||
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i + 3];
|
||||
else if (i + 1 == profile_new.size())
|
||||
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i - 1];
|
||||
else
|
||||
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + 0.5 * t * (profile_new[i - 1] + profile_new[i + 3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(layer_height_profile.size() > 2);
|
||||
assert(layer_height_profile.size() % 2 == 0);
|
||||
assert(layer_height_profile[0] == 0.);
|
||||
#ifdef _DEBUG
|
||||
for (size_t i = 2; i < layer_height_profile.size(); i += 2)
|
||||
assert(layer_height_profile[i - 2] <= layer_height_profile[i]);
|
||||
for (size_t i = 1; i < layer_height_profile.size(); i += 2) {
|
||||
assert(layer_height_profile[i] > slicing_params.min_layer_height - EPSILON);
|
||||
assert(layer_height_profile[i] < slicing_params.max_layer_height + EPSILON);
|
||||
}
|
||||
#endif /* _DEBUG */
|
||||
}
|
||||
|
||||
// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector.
|
||||
std::vector<coordf_t> generate_object_layers(
|
||||
const SlicingParameters &slicing_params,
|
||||
const std::vector<coordf_t> &layer_height_profile)
|
||||
{
|
||||
coordf_t print_z = 0;
|
||||
coordf_t height = 0;
|
||||
|
||||
std::vector<coordf_t> out;
|
||||
|
||||
if (slicing_params.first_object_layer_height_fixed()) {
|
||||
out.push_back(0);
|
||||
print_z = slicing_params.first_object_layer_height;
|
||||
out.push_back(print_z);
|
||||
}
|
||||
|
||||
size_t idx_layer_height_profile = 0;
|
||||
// loop until we have at least one layer and the max slice_z reaches the object height
|
||||
coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height;
|
||||
while (slice_z < slicing_params.object_print_z_height()) {
|
||||
height = slicing_params.min_layer_height;
|
||||
if (idx_layer_height_profile < layer_height_profile.size()) {
|
||||
size_t next = idx_layer_height_profile + 2;
|
||||
for (;;) {
|
||||
if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next])
|
||||
break;
|
||||
idx_layer_height_profile = next;
|
||||
next += 2;
|
||||
}
|
||||
coordf_t z1 = layer_height_profile[idx_layer_height_profile];
|
||||
coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1];
|
||||
height = h1;
|
||||
if (next < layer_height_profile.size()) {
|
||||
coordf_t z2 = layer_height_profile[next];
|
||||
coordf_t h2 = layer_height_profile[next + 1];
|
||||
height = lerp(h1, h2, (slice_z - z1) / (z2 - z1));
|
||||
assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON);
|
||||
}
|
||||
}
|
||||
slice_z = print_z + 0.5 * height;
|
||||
if (slice_z >= slicing_params.object_print_z_height())
|
||||
break;
|
||||
assert(height > slicing_params.min_layer_height - EPSILON);
|
||||
assert(height < slicing_params.max_layer_height + EPSILON);
|
||||
out.push_back(print_z);
|
||||
print_z += height;
|
||||
slice_z = print_z + 0.5 * slicing_params.min_layer_height;
|
||||
out.push_back(print_z);
|
||||
}
|
||||
|
||||
//FIXME Adjust the last layer to align with the top object layer exactly?
|
||||
return out;
|
||||
}
|
||||
|
||||
int generate_layer_height_texture(
|
||||
const SlicingParameters &slicing_params,
|
||||
const std::vector<coordf_t> &layers,
|
||||
void *data, int rows, int cols, bool level_of_detail_2nd_level)
|
||||
{
|
||||
// https://github.com/aschn/gnuplot-colorbrewer
|
||||
std::vector<Point3> palette_raw;
|
||||
palette_raw.push_back(Point3(0x0B2, 0x018, 0x02B));
|
||||
palette_raw.push_back(Point3(0x0D6, 0x060, 0x04D));
|
||||
palette_raw.push_back(Point3(0x0F4, 0x0A5, 0x082));
|
||||
palette_raw.push_back(Point3(0x0FD, 0x0DB, 0x0C7));
|
||||
palette_raw.push_back(Point3(0x0D1, 0x0E5, 0x0F0));
|
||||
palette_raw.push_back(Point3(0x092, 0x0C5, 0x0DE));
|
||||
palette_raw.push_back(Point3(0x043, 0x093, 0x0C3));
|
||||
palette_raw.push_back(Point3(0x021, 0x066, 0x0AC));
|
||||
|
||||
// Clear the main texture and the 2nd LOD level.
|
||||
memset(data, 0, rows * cols * 5);
|
||||
// 2nd LOD level data start
|
||||
unsigned char *data1 = reinterpret_cast<unsigned char*>(data) + rows * cols * 4;
|
||||
int ncells = std::min((cols-1) * rows, int(ceil(16. * (slicing_params.object_print_z_height() / slicing_params.min_layer_height))));
|
||||
int ncells1 = ncells / 2;
|
||||
int cols1 = cols / 2;
|
||||
coordf_t z_to_cell = coordf_t(ncells-1) / slicing_params.object_print_z_height();
|
||||
coordf_t cell_to_z = slicing_params.object_print_z_height() / coordf_t(ncells-1);
|
||||
coordf_t z_to_cell1 = coordf_t(ncells1-1) / slicing_params.object_print_z_height();
|
||||
coordf_t cell_to_z1 = slicing_params.object_print_z_height() / coordf_t(ncells1-1);
|
||||
// for color scaling
|
||||
coordf_t hscale = 2.f * std::max(slicing_params.max_layer_height - slicing_params.layer_height, slicing_params.layer_height - slicing_params.min_layer_height);
|
||||
if (hscale == 0)
|
||||
// All layers have the same height. Provide some height scale to avoid division by zero.
|
||||
hscale = slicing_params.layer_height;
|
||||
for (size_t idx_layer = 0; idx_layer < layers.size(); idx_layer += 2) {
|
||||
coordf_t lo = layers[idx_layer];
|
||||
coordf_t hi = layers[idx_layer + 1];
|
||||
coordf_t mid = 0.5f * (lo + hi);
|
||||
assert(mid <= slicing_params.object_print_z_height());
|
||||
coordf_t h = hi - lo;
|
||||
hi = std::min(hi, slicing_params.object_print_z_height());
|
||||
int cell_first = clamp(0, ncells-1, int(ceil(lo * z_to_cell)));
|
||||
int cell_last = clamp(0, ncells-1, int(floor(hi * z_to_cell)));
|
||||
for (int cell = cell_first; cell <= cell_last; ++ cell) {
|
||||
coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale;
|
||||
int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf)));
|
||||
int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1);
|
||||
coordf_t t = idxf - coordf_t(idx1);
|
||||
const Point3 &color1 = palette_raw[idx1];
|
||||
const Point3 &color2 = palette_raw[idx2];
|
||||
|
||||
coordf_t z = cell_to_z * coordf_t(cell);
|
||||
assert(z >= lo && z <= hi);
|
||||
// Intensity profile to visualize the layers.
|
||||
coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h);
|
||||
|
||||
// Color mapping from layer height to RGB.
|
||||
Pointf3 color(
|
||||
intensity * lerp(coordf_t(color1.x), coordf_t(color2.x), t),
|
||||
intensity * lerp(coordf_t(color1.y), coordf_t(color2.y), t),
|
||||
intensity * lerp(coordf_t(color1.z), coordf_t(color2.z), t));
|
||||
|
||||
int row = cell / (cols - 1);
|
||||
int col = cell - row * (cols - 1);
|
||||
assert(row >= 0 && row < rows);
|
||||
assert(col >= 0 && col < cols);
|
||||
unsigned char *ptr = (unsigned char*)data + (row * cols + col) * 4;
|
||||
ptr[0] = clamp<int>(0, 255, int(floor(color.x + 0.5)));
|
||||
ptr[1] = clamp<int>(0, 255, int(floor(color.y + 0.5)));
|
||||
ptr[2] = clamp<int>(0, 255, int(floor(color.z + 0.5)));
|
||||
ptr[3] = 255;
|
||||
if (col == 0 && row > 0) {
|
||||
// Duplicate the first value in a row as a last value of the preceding row.
|
||||
ptr[-4] = ptr[0];
|
||||
ptr[-3] = ptr[1];
|
||||
ptr[-2] = ptr[2];
|
||||
ptr[-1] = ptr[3];
|
||||
}
|
||||
}
|
||||
if (level_of_detail_2nd_level) {
|
||||
cell_first = clamp(0, ncells1-1, int(ceil(lo * z_to_cell1)));
|
||||
cell_last = clamp(0, ncells1-1, int(floor(hi * z_to_cell1)));
|
||||
for (int cell = cell_first; cell <= cell_last; ++ cell) {
|
||||
coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale;
|
||||
int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf)));
|
||||
int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1);
|
||||
coordf_t t = idxf - coordf_t(idx1);
|
||||
const Point3 &color1 = palette_raw[idx1];
|
||||
const Point3 &color2 = palette_raw[idx2];
|
||||
|
||||
coordf_t z = cell_to_z1 * coordf_t(cell);
|
||||
assert(z >= lo && z <= hi);
|
||||
|
||||
// Color mapping from layer height to RGB.
|
||||
Pointf3 color(
|
||||
lerp(coordf_t(color1.x), coordf_t(color2.x), t),
|
||||
lerp(coordf_t(color1.y), coordf_t(color2.y), t),
|
||||
lerp(coordf_t(color1.z), coordf_t(color2.z), t));
|
||||
|
||||
int row = cell / (cols1 - 1);
|
||||
int col = cell - row * (cols1 - 1);
|
||||
assert(row >= 0 && row < rows/2);
|
||||
assert(col >= 0 && col < cols/2);
|
||||
unsigned char *ptr = data1 + (row * cols1 + col) * 4;
|
||||
ptr[0] = clamp<int>(0, 255, int(floor(color.x + 0.5)));
|
||||
ptr[1] = clamp<int>(0, 255, int(floor(color.y + 0.5)));
|
||||
ptr[2] = clamp<int>(0, 255, int(floor(color.z + 0.5)));
|
||||
ptr[3] = 255;
|
||||
if (col == 0 && row > 0) {
|
||||
// Duplicate the first value in a row as a last value of the preceding row.
|
||||
ptr[-4] = ptr[0];
|
||||
ptr[-3] = ptr[1];
|
||||
ptr[-2] = ptr[2];
|
||||
ptr[-1] = ptr[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns number of cells of the 0th LOD level.
|
||||
return ncells;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
138
xs/src/libslic3r/Slicing.hpp
Normal file
138
xs/src/libslic3r/Slicing.hpp
Normal file
@ -0,0 +1,138 @@
|
||||
// Based on implementation by @platsch
|
||||
|
||||
#ifndef slic3r_Slicing_hpp_
|
||||
#define slic3r_Slicing_hpp_
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "libslic3r.h"
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
class PrintConfig;
|
||||
class PrintObjectConfig;
|
||||
class ModelVolume;
|
||||
typedef std::vector<ModelVolume*> ModelVolumePtrs;
|
||||
|
||||
// Parameters to guide object slicing and support generation.
|
||||
// The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow
|
||||
// (using a normal flow over a soluble support, using a bridging flow over a non-soluble support).
|
||||
struct SlicingParameters
|
||||
{
|
||||
SlicingParameters() { memset(this, 0, sizeof(SlicingParameters)); }
|
||||
|
||||
static SlicingParameters create_from_config(
|
||||
const PrintConfig &print_config,
|
||||
const PrintObjectConfig &object_config,
|
||||
coordf_t object_height,
|
||||
const std::set<size_t> &object_extruders);
|
||||
|
||||
// Has any raft layers?
|
||||
bool has_raft() const { return raft_layers() > 0; }
|
||||
size_t raft_layers() const { return base_raft_layers + interface_raft_layers; }
|
||||
|
||||
// Is the 1st object layer height fixed, or could it be varied?
|
||||
bool first_object_layer_height_fixed() const { return ! has_raft() || first_object_layer_bridging; }
|
||||
|
||||
// Height of the object to be printed. This value does not contain the raft height.
|
||||
coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; }
|
||||
|
||||
// Number of raft layers.
|
||||
size_t base_raft_layers;
|
||||
// Number of interface layers including the contact layer.
|
||||
size_t interface_raft_layers;
|
||||
|
||||
// Layer heights of the raft (base, interface and a contact layer).
|
||||
coordf_t base_raft_layer_height;
|
||||
coordf_t interface_raft_layer_height;
|
||||
coordf_t contact_raft_layer_height;
|
||||
bool contact_raft_layer_height_bridging;
|
||||
|
||||
// The regular layer height, applied for all but the first layer, if not overridden by layer ranges
|
||||
// or by the variable layer thickness table.
|
||||
coordf_t layer_height;
|
||||
|
||||
// First layer height of the print, this may be used for the first layer of the raft
|
||||
// or for the first layer of the print.
|
||||
coordf_t first_print_layer_height;
|
||||
|
||||
// Thickness of the first layer. This is either the first print layer thickness if printed without a raft,
|
||||
// or a bridging flow thickness if printed over a non-soluble raft,
|
||||
// or a normal layer height if printed over a soluble raft.
|
||||
coordf_t first_object_layer_height;
|
||||
|
||||
// If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow.
|
||||
bool first_object_layer_bridging;
|
||||
|
||||
// Soluble interface? (PLA soluble in water, HIPS soluble in lemonen)
|
||||
// otherwise the interface must be broken off.
|
||||
bool soluble_interface;
|
||||
// Gap when placing object over raft.
|
||||
coordf_t gap_raft_object;
|
||||
// Gap when placing support over object.
|
||||
coordf_t gap_object_support;
|
||||
// Gap when placing object over support.
|
||||
coordf_t gap_support_object;
|
||||
|
||||
// Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm,
|
||||
// or by an interactive layer height editor.
|
||||
coordf_t min_layer_height;
|
||||
coordf_t max_layer_height;
|
||||
|
||||
// Bottom and top of the printed object.
|
||||
// If printed without a raft, object_print_z_min = 0 and object_print_z_max = object height.
|
||||
// Otherwise object_print_z_min is equal to the raft height.
|
||||
coordf_t raft_base_top_z;
|
||||
coordf_t raft_interface_top_z;
|
||||
coordf_t raft_contact_top_z;
|
||||
// In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer.
|
||||
coordf_t object_print_z_min;
|
||||
coordf_t object_print_z_max;
|
||||
};
|
||||
|
||||
typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
|
||||
typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
|
||||
|
||||
extern std::vector<coordf_t> layer_height_profile_from_ranges(
|
||||
const SlicingParameters &slicing_params,
|
||||
const t_layer_height_ranges &layer_height_ranges);
|
||||
|
||||
extern std::vector<coordf_t> layer_height_profile_adaptive(
|
||||
const SlicingParameters &slicing_params,
|
||||
const t_layer_height_ranges &layer_height_ranges,
|
||||
const ModelVolumePtrs &volumes);
|
||||
|
||||
|
||||
enum LayerHeightEditActionType {
|
||||
LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0,
|
||||
LAYER_HEIGHT_EDIT_ACTION_DECREASE = 1,
|
||||
LAYER_HEIGHT_EDIT_ACTION_REDUCE = 2,
|
||||
LAYER_HEIGHT_EDIT_ACTION_SMOOTH = 3
|
||||
};
|
||||
|
||||
extern void adjust_layer_height_profile(
|
||||
const SlicingParameters &slicing_params,
|
||||
std::vector<coordf_t> &layer_height_profile,
|
||||
coordf_t z,
|
||||
coordf_t layer_thickness_delta,
|
||||
coordf_t band_width,
|
||||
LayerHeightEditActionType action);
|
||||
|
||||
// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector.
|
||||
// The object layers are based at z=0, ignoring the raft layers.
|
||||
extern std::vector<coordf_t> generate_object_layers(
|
||||
const SlicingParameters &slicing_params,
|
||||
const std::vector<coordf_t> &layer_height_profile);
|
||||
|
||||
// Produce a 1D texture packed into a 2D texture describing in the RGBA format
|
||||
// the planned object layers.
|
||||
// Returns number of cells used by the texture of the 0th LOD level.
|
||||
extern int generate_layer_height_texture(
|
||||
const SlicingParameters &slicing_params,
|
||||
const std::vector<coordf_t> &layers,
|
||||
void *data, int rows, int cols, bool level_of_detail_2nd_level);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Slicing_hpp_ */
|
140
xs/src/libslic3r/SlicingAdaptive.cpp
Normal file
140
xs/src/libslic3r/SlicingAdaptive.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
#include "libslic3r.h"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "SlicingAdaptive.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
void SlicingAdaptive::clear()
|
||||
{
|
||||
m_meshes.clear();
|
||||
m_faces.clear();
|
||||
m_face_normal_z.clear();
|
||||
}
|
||||
|
||||
std::pair<float, float> face_z_span(const stl_facet *f)
|
||||
{
|
||||
return std::pair<float, float>(
|
||||
std::min(std::min(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z),
|
||||
std::max(std::max(f->vertex[0].z, f->vertex[1].z), f->vertex[2].z));
|
||||
}
|
||||
|
||||
void SlicingAdaptive::prepare()
|
||||
{
|
||||
// 1) Collect faces of all meshes.
|
||||
int nfaces_total = 0;
|
||||
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
|
||||
nfaces_total += (*it_mesh)->stl.stats.number_of_facets;
|
||||
m_faces.reserve(nfaces_total);
|
||||
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
|
||||
for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i)
|
||||
m_faces.push_back((*it_mesh)->stl.facet_start + i);
|
||||
|
||||
// 2) Sort faces lexicographically by their Z span.
|
||||
std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) {
|
||||
std::pair<float, float> span1 = face_z_span(f1);
|
||||
std::pair<float, float> span2 = face_z_span(f2);
|
||||
return span1 < span2;
|
||||
});
|
||||
|
||||
// 3) Generate Z components of the facet normals.
|
||||
m_face_normal_z.assign(m_faces.size(), 0.f);
|
||||
for (size_t iface = 0; iface < m_faces.size(); ++ iface)
|
||||
m_face_normal_z[iface] = m_faces[iface]->normal.z;
|
||||
}
|
||||
|
||||
float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet)
|
||||
{
|
||||
float height = m_slicing_params.max_layer_height;
|
||||
bool first_hit = false;
|
||||
|
||||
// find all facets intersecting the slice-layer
|
||||
int ordered_id = current_facet;
|
||||
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
|
||||
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
|
||||
// facet's minimum is higher than slice_z -> end loop
|
||||
if (zspan.first >= z)
|
||||
break;
|
||||
// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
|
||||
if (zspan.second > z) {
|
||||
// first event?
|
||||
if (! first_hit) {
|
||||
first_hit = true;
|
||||
current_facet = ordered_id;
|
||||
}
|
||||
// skip touching facets which could otherwise cause small cusp values
|
||||
if (zspan.second <= z + EPSILON)
|
||||
continue;
|
||||
// compute cusp-height for this facet and store minimum of all heights
|
||||
float normal_z = m_face_normal_z[ordered_id];
|
||||
height = std::min(height, (normal_z == 0.f) ? 9999.f : std::abs(cusp_value / normal_z));
|
||||
}
|
||||
}
|
||||
|
||||
// lower height limit due to printer capabilities
|
||||
height = std::max(height, float(m_slicing_params.min_layer_height));
|
||||
|
||||
// check for sloped facets inside the determined layer and correct height if necessary
|
||||
if (height > m_slicing_params.min_layer_height) {
|
||||
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
|
||||
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
|
||||
// facet's minimum is higher than slice_z + height -> end loop
|
||||
if (zspan.first >= z + height)
|
||||
break;
|
||||
|
||||
// skip touching facets which could otherwise cause small cusp values
|
||||
if (zspan.second <= z + EPSILON)
|
||||
continue;
|
||||
|
||||
// Compute cusp-height for this facet and check against height.
|
||||
float normal_z = m_face_normal_z[ordered_id];
|
||||
float cusp = (normal_z == 0) ? 9999 : abs(cusp_value / normal_z);
|
||||
|
||||
float z_diff = zspan.first - z;
|
||||
|
||||
// handle horizontal facets
|
||||
if (m_face_normal_z[ordered_id] > 0.999) {
|
||||
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
|
||||
height = z_diff;
|
||||
// Slic3r::debugf "to %f due to near horizontal facet\n", $height;
|
||||
} else if (cusp > z_diff) {
|
||||
if (cusp < height) {
|
||||
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
|
||||
height = cusp;
|
||||
// Slic3r::debugf "to %f due to new cusp height\n", $height;
|
||||
}
|
||||
} else {
|
||||
// Slic3r::debugf "cusp computation, height is reduced from %f", $height;
|
||||
height = z_diff;
|
||||
// Slic3r::debugf "to z-diff: %f\n", $height;
|
||||
}
|
||||
}
|
||||
// lower height limit due to printer capabilities again
|
||||
height = std::max(height, float(m_slicing_params.min_layer_height));
|
||||
}
|
||||
|
||||
// Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height;
|
||||
return height;
|
||||
}
|
||||
|
||||
// Returns the distance to the next horizontal facet in Z-dir
|
||||
// to consider horizontal object features in slice thickness
|
||||
float SlicingAdaptive::horizontal_facet_distance(float z)
|
||||
{
|
||||
for (size_t i = 0; i < m_faces.size(); ++ i) {
|
||||
std::pair<float, float> zspan = face_z_span(m_faces[i]);
|
||||
// facet's minimum is higher than max forward distance -> end loop
|
||||
if (zspan.first > z + m_slicing_params.max_layer_height)
|
||||
break;
|
||||
// min_z == max_z -> horizontal facet
|
||||
if (zspan.first > z && zspan.first == zspan.second)
|
||||
return zspan.first - z;
|
||||
}
|
||||
|
||||
// objects maximum?
|
||||
return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ?
|
||||
std::max<float>(m_slicing_params.object_print_z_height() - z, 0.f) :
|
||||
m_slicing_params.max_layer_height;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
36
xs/src/libslic3r/SlicingAdaptive.hpp
Normal file
36
xs/src/libslic3r/SlicingAdaptive.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
// Based on implementation by @platsch
|
||||
|
||||
#ifndef slic3r_SlicingAdaptive_hpp_
|
||||
#define slic3r_SlicingAdaptive_hpp_
|
||||
|
||||
#include "Slicing.hpp"
|
||||
#include "admesh/stl.h"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
class TriangleMesh;
|
||||
|
||||
class SlicingAdaptive
|
||||
{
|
||||
public:
|
||||
void clear();
|
||||
void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; }
|
||||
void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); }
|
||||
void prepare();
|
||||
float cusp_height(float z, float cusp_value, int ¤t_facet);
|
||||
float horizontal_facet_distance(float z);
|
||||
|
||||
protected:
|
||||
SlicingParameters m_slicing_params;
|
||||
|
||||
std::vector<const TriangleMesh*> m_meshes;
|
||||
// Collected faces of all meshes, sorted by raising Z of the bottom most face.
|
||||
std::vector<const stl_facet*> m_faces;
|
||||
// Z component of face normals, normalized.
|
||||
std::vector<float> m_face_normal_z;
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_SlicingAdaptive_hpp_ */
|
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,7 @@
|
||||
|
||||
#include "Flow.hpp"
|
||||
#include "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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -28,8 +28,27 @@ class SurfaceCollection
|
||||
void remove_type(const SurfaceType type);
|
||||
void remove_types(const SurfaceType *types, int ntypes);
|
||||
void filter_by_type(SurfaceType type, Polygons* polygons);
|
||||
void append(const SurfaceCollection &coll);
|
||||
void append(const SurfaceType surfaceType, const ExPolygons &expoly);
|
||||
|
||||
void clear() { surfaces.clear(); }
|
||||
bool empty() const { return surfaces.empty(); }
|
||||
|
||||
void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
|
||||
void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
|
||||
void set(const ExPolygons &src, SurfaceType surfaceType) { clear(); this->append(src, surfaceType); }
|
||||
void set(const ExPolygons &src, const Surface &surfaceTempl) { clear(); this->append(src, surfaceTempl); }
|
||||
void set(const Surfaces &src) { clear(); this->append(src); }
|
||||
void set(ExPolygons &&src, SurfaceType surfaceType) { clear(); this->append(std::move(src), surfaceType); }
|
||||
void set(ExPolygons &&src, const Surface &surfaceTempl) { clear(); this->append(std::move(src), surfaceTempl); }
|
||||
void set(Surfaces &&src) { clear(); this->append(std::move(src)); }
|
||||
|
||||
void append(const SurfaceCollection &coll) { this->append(coll.surfaces); }
|
||||
void append(SurfaceCollection &&coll) { this->append(std::move(coll.surfaces)); }
|
||||
void append(const ExPolygons &src, SurfaceType surfaceType) { surfaces_append(this->surfaces, src, surfaceType); }
|
||||
void append(const ExPolygons &src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, src, surfaceTempl); }
|
||||
void append(const Surfaces &src) { surfaces_append(this->surfaces, src); }
|
||||
void append(ExPolygons &&src, SurfaceType surfaceType) { surfaces_append(this->surfaces, std::move(src), surfaceType); }
|
||||
void append(ExPolygons &&src, const Surface &surfaceTempl) { surfaces_append(this->surfaces, std::move(src), surfaceTempl); }
|
||||
void append(Surfaces &&src) { surfaces_append(this->surfaces, std::move(src)); }
|
||||
|
||||
// For debugging purposes:
|
||||
void export_to_svg(const char *path, bool show_labels);
|
||||
|
@ -2,8 +2,8 @@
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include <cmath>
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
@ -193,12 +193,17 @@ void TriangleMesh::scale(const Pointf3 &versor)
|
||||
|
||||
void TriangleMesh::translate(float x, float y, float z)
|
||||
{
|
||||
if (x == 0.f && y == 0.f && z == 0.f)
|
||||
return;
|
||||
stl_translate_relative(&(this->stl), x, y, z);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::rotate(float angle, const Axis &axis)
|
||||
{
|
||||
if (angle == 0.f)
|
||||
return;
|
||||
|
||||
// admesh uses degrees
|
||||
angle = Slic3r::Geometry::rad2deg(angle);
|
||||
|
||||
@ -265,6 +270,8 @@ void TriangleMesh::align_to_origin()
|
||||
|
||||
void TriangleMesh::rotate(double angle, Point* center)
|
||||
{
|
||||
if (angle == 0.)
|
||||
return;
|
||||
this->translate(-center->x, -center->y, 0);
|
||||
stl_rotate_z(&(this->stl), (float)angle);
|
||||
this->translate(+center->x, +center->y, 0);
|
||||
@ -363,10 +370,7 @@ TriangleMesh::horizontal_projection() const
|
||||
}
|
||||
|
||||
// the offset factor was tuned using groovemount.stl
|
||||
offset(pp, &pp, 0.01 / SCALING_FACTOR);
|
||||
ExPolygons retval;
|
||||
union_(pp, &retval, true);
|
||||
return retval;
|
||||
return union_ex(offset(pp, 0.01 / SCALING_FACTOR), true);
|
||||
}
|
||||
|
||||
Polygon
|
||||
@ -403,7 +407,7 @@ TriangleMesh::require_shared_vertices()
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers)
|
||||
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
|
||||
{
|
||||
/*
|
||||
This method gets called with a list of unscaled Z coordinates and outputs
|
||||
@ -427,58 +431,66 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
|
||||
|
||||
At the end, we free the tables generated by analyze() as we don't
|
||||
need them anymore.
|
||||
FUTURE: parallelize slice_facet() and make_loops()
|
||||
|
||||
NOTE: this method accepts a vector of floats because the mesh coordinate
|
||||
type is float.
|
||||
*/
|
||||
|
||||
std::vector<IntersectionLines> lines(z.size());
|
||||
|
||||
for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) {
|
||||
stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
|
||||
|
||||
// find facet extents
|
||||
float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z));
|
||||
float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z));
|
||||
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
|
||||
facet->vertex[0].x, facet->vertex[0].y, facet->vertex[0].z,
|
||||
facet->vertex[1].x, facet->vertex[1].y, facet->vertex[1].z,
|
||||
facet->vertex[2].x, facet->vertex[2].y, facet->vertex[2].z);
|
||||
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
|
||||
#endif
|
||||
|
||||
// find layer extents
|
||||
std::vector<float>::const_iterator min_layer, max_layer;
|
||||
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
|
||||
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
|
||||
#endif
|
||||
|
||||
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
|
||||
std::vector<float>::size_type layer_idx = it - z.begin();
|
||||
this->slice_facet(*it / SCALING_FACTOR, *facet, facet_idx, min_z, max_z, &lines[layer_idx]);
|
||||
}
|
||||
{
|
||||
boost::mutex lines_mutex;
|
||||
parallelize<int>(
|
||||
0,
|
||||
this->mesh->stl.stats.number_of_facets-1,
|
||||
boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z)
|
||||
);
|
||||
}
|
||||
|
||||
// v_scaled_shared could be freed here
|
||||
|
||||
// build loops
|
||||
layers->resize(z.size());
|
||||
for (std::vector<IntersectionLines>::iterator it = lines.begin(); it != lines.end(); ++it) {
|
||||
size_t layer_idx = it - lines.begin();
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf("Layer " PRINTF_ZU ":\n", layer_idx);
|
||||
#endif
|
||||
this->make_loops(*it, &(*layers)[layer_idx]);
|
||||
parallelize<size_t>(
|
||||
0,
|
||||
lines.size()-1,
|
||||
boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers)
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex,
|
||||
const std::vector<float> &z) const
|
||||
{
|
||||
const stl_facet &facet = this->mesh->stl.facet_start[facet_idx];
|
||||
|
||||
// find facet extents
|
||||
const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z));
|
||||
const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z));
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
|
||||
facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z,
|
||||
facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z,
|
||||
facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z);
|
||||
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
|
||||
#endif
|
||||
|
||||
// find layer extents
|
||||
std::vector<float>::const_iterator min_layer, max_layer;
|
||||
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
|
||||
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
|
||||
#endif
|
||||
|
||||
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
|
||||
std::vector<float>::size_type layer_idx = it - z.begin();
|
||||
this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers)
|
||||
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
|
||||
{
|
||||
std::vector<Polygons> layers_p;
|
||||
this->slice(z, &layers_p);
|
||||
@ -495,7 +507,9 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>*
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines) const
|
||||
TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx,
|
||||
const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines,
|
||||
boost::mutex* lines_mutex) const
|
||||
{
|
||||
std::vector<IntersectionPoint> points;
|
||||
std::vector< std::vector<IntersectionPoint>::size_type > points_on_layer;
|
||||
@ -547,7 +561,12 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
|
||||
line.b.y = b->y;
|
||||
line.a_id = a_id;
|
||||
line.b_id = b_id;
|
||||
lines->push_back(line);
|
||||
if (lines_mutex != NULL) {
|
||||
boost::lock_guard<boost::mutex> l(*lines_mutex);
|
||||
lines->push_back(line);
|
||||
} else {
|
||||
lines->push_back(line);
|
||||
}
|
||||
|
||||
found_horizontal_edge = true;
|
||||
|
||||
@ -600,13 +619,24 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
|
||||
line.b_id = points[0].point_id;
|
||||
line.edge_a_id = points[1].edge_id;
|
||||
line.edge_b_id = points[0].edge_id;
|
||||
lines->push_back(line);
|
||||
if (lines_mutex != NULL) {
|
||||
boost::lock_guard<boost::mutex> l(*lines_mutex);
|
||||
lines->push_back(line);
|
||||
} else {
|
||||
lines->push_back(line);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
|
||||
TriangleMeshSlicer::_make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const
|
||||
{
|
||||
this->make_loops((*lines)[i], &(*layers)[i]);
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
|
||||
{
|
||||
/*
|
||||
SVG svg("lines.svg");
|
||||
@ -707,6 +737,7 @@ TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* l
|
||||
for (IntersectionLinePtrs::const_iterator lineptr = loop.begin(); lineptr != loop.end(); ++lineptr) {
|
||||
p.points.push_back((*lineptr)->a);
|
||||
}
|
||||
|
||||
loops->push_back(p);
|
||||
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
@ -746,7 +777,7 @@ class _area_comp {
|
||||
};
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices)
|
||||
TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
|
||||
{
|
||||
Polygons loops;
|
||||
this->make_loops(lines, &loops);
|
||||
@ -780,7 +811,7 @@ TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &lines,
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
|
||||
TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const
|
||||
{
|
||||
/*
|
||||
Input loops are not suitable for evenodd nor nonzero fill types, as we might get
|
||||
@ -818,17 +849,15 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
|
||||
of the loops, since the Orientation() function provided by Clipper
|
||||
would do the same, thus repeating the calculation */
|
||||
Polygons::const_iterator loop = loops.begin() + *loop_idx;
|
||||
if (area[*loop_idx] > +EPSILON) {
|
||||
if (area[*loop_idx] > +EPSILON)
|
||||
p_slices.push_back(*loop);
|
||||
} else if (area[*loop_idx] < -EPSILON) {
|
||||
diff(p_slices, *loop, &p_slices);
|
||||
}
|
||||
else if (area[*loop_idx] < -EPSILON)
|
||||
p_slices = diff(p_slices, *loop);
|
||||
}
|
||||
|
||||
// perform a safety offset to merge very close facets (TODO: find test case for this)
|
||||
double safety_offset = scale_(0.0499);
|
||||
ExPolygons ex_slices;
|
||||
offset2(p_slices, &ex_slices, +safety_offset, -safety_offset);
|
||||
ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset);
|
||||
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
size_t holes_count = 0;
|
||||
@ -840,11 +869,11 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
|
||||
#endif
|
||||
|
||||
// append to the supplied collection
|
||||
slices->insert(slices->end(), ex_slices.begin(), ex_slices.end());
|
||||
expolygons_append(*slices, ex_slices);
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices)
|
||||
TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
|
||||
{
|
||||
Polygons pp;
|
||||
this->make_loops(lines, &pp);
|
||||
@ -852,7 +881,7 @@ TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPoly
|
||||
}
|
||||
|
||||
void
|
||||
TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
|
||||
TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const
|
||||
{
|
||||
IntersectionLines upper_lines, lower_lines;
|
||||
|
||||
@ -1004,7 +1033,6 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
|
||||
|
||||
stl_get_size(&(upper->stl));
|
||||
stl_get_size(&(lower->stl));
|
||||
|
||||
}
|
||||
|
||||
TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL)
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "libslic3r.h"
|
||||
#include <admesh/stl.h>
|
||||
#include <vector>
|
||||
#include <boost/thread.hpp>
|
||||
#include "BoundingBox.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "Point.hpp"
|
||||
@ -88,19 +89,23 @@ class TriangleMeshSlicer
|
||||
TriangleMesh* mesh;
|
||||
TriangleMeshSlicer(TriangleMesh* _mesh);
|
||||
~TriangleMeshSlicer();
|
||||
void slice(const std::vector<float> &z, std::vector<Polygons>* layers);
|
||||
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers);
|
||||
void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines) const;
|
||||
void cut(float z, TriangleMesh* upper, TriangleMesh* lower);
|
||||
void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const;
|
||||
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const;
|
||||
void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx,
|
||||
const float &min_z, const float &max_z, std::vector<IntersectionLine>* lines,
|
||||
boost::mutex* lines_mutex = NULL) const;
|
||||
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
|
||||
|
||||
private:
|
||||
typedef std::vector< std::vector<int> > t_facets_edges;
|
||||
t_facets_edges facets_edges;
|
||||
stl_vertex* v_scaled_shared;
|
||||
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops);
|
||||
void make_expolygons(const Polygons &loops, ExPolygons* slices);
|
||||
void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices);
|
||||
void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices);
|
||||
void _slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, const std::vector<float> &z) const;
|
||||
void _make_loops_do(size_t i, std::vector<IntersectionLines>* lines, std::vector<Polygons>* layers) const;
|
||||
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const;
|
||||
void make_expolygons(const Polygons &loops, ExPolygons* slices) const;
|
||||
void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
|
||||
void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
10
xs/src/libslic3r/Utils.hpp
Normal file
10
xs/src/libslic3r/Utils.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef slic3r_Utils_hpp_
|
||||
#define slic3r_Utils_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
extern void set_logging_level(unsigned int level);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_Utils_hpp_
|
@ -4,10 +4,14 @@
|
||||
// this needs to be included early for MSVC (listing it in Build.PL is not enough)
|
||||
#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
|
||||
|
@ -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
|
||||
|
@ -532,8 +532,7 @@ SV*
|
||||
polynode2perl(const ClipperLib::PolyNode& node)
|
||||
{
|
||||
HV* hv = newHV();
|
||||
Slic3r::Polygon p;
|
||||
ClipperPath_to_Slic3rMultiPoint(node.Contour, &p);
|
||||
Slic3r::Polygon p = ClipperPath_to_Slic3rPolygon(node.Contour);
|
||||
if (node.IsHole()) {
|
||||
(void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) );
|
||||
} else {
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <libslic3r.h>
|
||||
|
||||
#ifdef SLIC3RXS
|
||||
extern "C" {
|
||||
@ -42,15 +43,18 @@ extern "C" {
|
||||
#undef do_close
|
||||
#undef bind
|
||||
#undef seed
|
||||
#undef push
|
||||
#undef pop
|
||||
#ifdef _MSC_VER
|
||||
// Undef some of the macros set by Perl <xsinit.h>, which cause compilation errors on Win32
|
||||
#undef send
|
||||
#undef connect
|
||||
#undef seek
|
||||
#undef send
|
||||
#undef write
|
||||
#endif /* _MSC_VER */
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <libslic3r.h>
|
||||
#include <ClipperUtils.hpp>
|
||||
#include <Config.hpp>
|
||||
#include <ExPolygon.hpp>
|
||||
@ -163,4 +167,6 @@ SV* polynode2perl(const ClipperLib::PolyNode& node);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
#endif
|
||||
|
@ -5,7 +5,7 @@ use warnings;
|
||||
|
||||
use List::Util qw(sum);
|
||||
use Slic3r::XS;
|
||||
use Test::More tests => 23;
|
||||
use Test::More tests => 16;
|
||||
|
||||
my $square = Slic3r::Polygon->new( # ccw
|
||||
[200, 100],
|
||||
@ -121,41 +121,6 @@ if (0) { # Clipper does not preserve polyline orientation
|
||||
is_deeply $result->[0]->pp, [[200,150], [100,150]], 'clipped line orientation is preserved';
|
||||
}
|
||||
|
||||
if (0) { # Clipper does not preserve polyline orientation
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$hole_in_square], [$square]);
|
||||
is_deeply $result->[0]->pp, $hole_in_square->split_at_first_point->pp,
|
||||
'intersection_ppl - clipping cw polygon as polyline preserves winding order';
|
||||
}
|
||||
|
||||
{
|
||||
my $square2 = $square->clone;
|
||||
$square2->translate(50,50);
|
||||
{
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
|
||||
is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
|
||||
is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
|
||||
# Clipper does not preserve polyline orientation so we only check the middle point
|
||||
###ok $result->[0][0]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
|
||||
ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
|
||||
###ok $result->[0][2]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
my $square2 = $square->clone;
|
||||
$square2->reverse;
|
||||
$square2->translate(50,50);
|
||||
{
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
|
||||
is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
|
||||
is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
|
||||
# Clipper does not preserve polyline orientation so we only check the middle point
|
||||
###ok $result->[0][0]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
|
||||
ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
|
||||
###ok $result->[0][2]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
# Clipper bug #96 (our issue #2028)
|
||||
my $subject = Slic3r::Polyline->new(
|
||||
@ -168,17 +133,6 @@ if (0) { # Clipper does not preserve polyline orientation
|
||||
is scalar(@$result), 1, 'intersection_pl - result is not empty';
|
||||
}
|
||||
|
||||
{
|
||||
my $subject = Slic3r::Polygon->new(
|
||||
[44730000,31936670],[55270000,31936670],[55270000,25270000],[74730000,25270000],[74730000,44730000],[68063296,44730000],[68063296,55270000],[74730000,55270000],[74730000,74730000],[55270000,74730000],[55270000,68063296],[44730000,68063296],[44730000,74730000],[25270000,74730000],[25270000,55270000],[31936670,55270000],[31936670,44730000],[25270000,44730000],[25270000,25270000],[44730000,25270000]
|
||||
);
|
||||
my $clip = [
|
||||
Slic3r::Polygon->new([75200000,45200000],[54800000,45200000],[54800000,24800000],[75200000,24800000]),
|
||||
];
|
||||
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$subject], $clip);
|
||||
is scalar(@$result), 1, 'intersection_ppl - result is not empty';
|
||||
}
|
||||
|
||||
{
|
||||
# Clipper bug #122
|
||||
my $subject = [
|
||||
|
@ -30,7 +30,8 @@
|
||||
long y_min() %code{% RETVAL = THIS->min.y; %};
|
||||
long y_max() %code{% RETVAL = THIS->max.y; %};
|
||||
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%ld,%ld;%ld,%ld", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %};
|
||||
|
||||
bool defined() %code{% RETVAL = THIS->defined; %};
|
||||
|
||||
%{
|
||||
|
||||
BoundingBox*
|
||||
@ -69,7 +70,8 @@ new_from_points(CLASS, points)
|
||||
void set_y_min(double val) %code{% THIS->min.y = val; %};
|
||||
void set_y_max(double val) %code{% THIS->max.y = val; %};
|
||||
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf;%lf,%lf", THIS->min.x, THIS->min.y, THIS->max.x, THIS->max.y); RETVAL = buf; %};
|
||||
|
||||
bool defined() %code{% RETVAL = THIS->defined; %};
|
||||
|
||||
%{
|
||||
|
||||
BoundingBoxf*
|
||||
@ -106,4 +108,5 @@ new_from_points(CLASS, points)
|
||||
double z_min() %code{% RETVAL = THIS->min.z; %};
|
||||
double z_max() %code{% RETVAL = THIS->max.z; %};
|
||||
std::string serialize() %code{% char buf[2048]; sprintf(buf, "%lf,%lf,%lf;%lf,%lf,%lf", THIS->min.x, THIS->min.y, THIS->min.z, THIS->max.x, THIS->max.y, THIS->max.z); RETVAL = buf; %};
|
||||
bool defined() %code{% RETVAL = THIS->defined; %};
|
||||
};
|
||||
|
@ -16,58 +16,53 @@ _constant()
|
||||
JT_MITER = jtMiter
|
||||
JT_ROUND = jtRound
|
||||
JT_SQUARE = jtSquare
|
||||
CLIPPER_OFFSET_SCALE = CLIPPER_OFFSET_SCALE
|
||||
CODE:
|
||||
RETVAL = ix;
|
||||
OUTPUT: RETVAL
|
||||
|
||||
Polygons
|
||||
offset(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset(polygons, &RETVAL, delta, scale, joinType, miterLimit);
|
||||
RETVAL = offset(polygons, delta, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
ExPolygons
|
||||
offset_ex(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset_ex(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset(polygons, &RETVAL, delta, scale, joinType, miterLimit);
|
||||
RETVAL = offset_ex(polygons, delta, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Polygons
|
||||
offset2(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset2(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta1
|
||||
const float delta2
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit);
|
||||
RETVAL = offset2(polygons, delta1, delta2, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
ExPolygons
|
||||
offset2_ex(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
offset2_ex(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3)
|
||||
Polygons polygons
|
||||
const float delta1
|
||||
const float delta2
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit);
|
||||
RETVAL = offset2_ex(polygons, delta1, delta2, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
@ -77,7 +72,7 @@ diff(subject, clip, safety_offset = false)
|
||||
Polygons clip
|
||||
bool safety_offset
|
||||
CODE:
|
||||
diff(subject, clip, &RETVAL, safety_offset);
|
||||
RETVAL = diff(subject, clip, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
@ -87,7 +82,7 @@ diff_ex(subject, clip, safety_offset = false)
|
||||
Polygons clip
|
||||
bool safety_offset
|
||||
CODE:
|
||||
diff(subject, clip, &RETVAL, safety_offset);
|
||||
RETVAL = diff_ex(subject, clip, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
@ -96,16 +91,7 @@ diff_pl(subject, clip)
|
||||
Polylines subject
|
||||
Polygons clip
|
||||
CODE:
|
||||
diff(subject, clip, &RETVAL);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Polylines
|
||||
diff_ppl(subject, clip)
|
||||
Polygons subject
|
||||
Polygons clip
|
||||
CODE:
|
||||
diff(subject, clip, &RETVAL);
|
||||
RETVAL = diff_pl(subject, clip);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
@ -115,7 +101,7 @@ intersection(subject, clip, safety_offset = false)
|
||||
Polygons clip
|
||||
bool safety_offset
|
||||
CODE:
|
||||
intersection(subject, clip, &RETVAL, safety_offset);
|
||||
RETVAL = intersection(subject, clip, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
@ -125,7 +111,7 @@ intersection_ex(subject, clip, safety_offset = false)
|
||||
Polygons clip
|
||||
bool safety_offset
|
||||
CODE:
|
||||
intersection(subject, clip, &RETVAL, safety_offset);
|
||||
RETVAL = intersection_ex(subject, clip, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
@ -134,26 +120,7 @@ intersection_pl(subject, clip)
|
||||
Polylines subject
|
||||
Polygons clip
|
||||
CODE:
|
||||
intersection(subject, clip, &RETVAL);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
Polylines
|
||||
intersection_ppl(subject, clip)
|
||||
Polygons subject
|
||||
Polygons clip
|
||||
CODE:
|
||||
intersection(subject, clip, &RETVAL);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
ExPolygons
|
||||
xor_ex(subject, clip, safety_offset = false)
|
||||
Polygons subject
|
||||
Polygons clip
|
||||
bool safety_offset
|
||||
CODE:
|
||||
xor_(subject, clip, &RETVAL, safety_offset);
|
||||
RETVAL = intersection_pl(subject, clip);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
@ -162,7 +129,7 @@ union(subject, safety_offset = false)
|
||||
Polygons subject
|
||||
bool safety_offset
|
||||
CODE:
|
||||
union_(subject, &RETVAL, safety_offset);
|
||||
RETVAL = union_(subject, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
@ -171,20 +138,7 @@ union_ex(subject, safety_offset = false)
|
||||
Polygons subject
|
||||
bool safety_offset
|
||||
CODE:
|
||||
union_(subject, &RETVAL, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
SV*
|
||||
union_pt(subject, safety_offset = false)
|
||||
Polygons subject
|
||||
bool safety_offset
|
||||
CODE:
|
||||
// perform operation
|
||||
ClipperLib::PolyTree polytree;
|
||||
union_pt(subject, &polytree, safety_offset);
|
||||
|
||||
RETVAL = polynode_children_2_perl(polytree);
|
||||
RETVAL = union_ex(subject, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
@ -193,7 +147,7 @@ union_pt_chained(subject, safety_offset = false)
|
||||
Polygons subject
|
||||
bool safety_offset
|
||||
CODE:
|
||||
union_pt_chained(subject, &RETVAL, safety_offset);
|
||||
RETVAL = union_pt_chained(subject, safety_offset);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
@ -201,7 +155,7 @@ Polygons
|
||||
simplify_polygons(subject)
|
||||
Polygons subject
|
||||
CODE:
|
||||
simplify_polygons(subject, &RETVAL);
|
||||
RETVAL = simplify_polygons(subject);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -80,13 +80,12 @@ Polyline::rotate(angle, center_sv)
|
||||
THIS->rotate(angle, center);
|
||||
|
||||
Polygons
|
||||
Polyline::grow(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtSquare, miterLimit = 3)
|
||||
Polyline::grow(delta, joinType = ClipperLib::jtSquare, miterLimit = 3)
|
||||
const float delta
|
||||
double scale
|
||||
ClipperLib::JoinType joinType
|
||||
double miterLimit
|
||||
CODE:
|
||||
offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit);
|
||||
RETVAL = offset(*THIS, delta, joinType, miterLimit);
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/Slicing.hpp"
|
||||
#include "libslic3r/PlaceholderParser.hpp"
|
||||
%}
|
||||
|
||||
@ -58,6 +59,8 @@ _constant()
|
||||
Points copies();
|
||||
t_layer_height_ranges layer_height_ranges()
|
||||
%code%{ RETVAL = THIS->layer_height_ranges; %};
|
||||
std::vector<double> layer_height_profile()
|
||||
%code%{ RETVAL = THIS->layer_height_profile; %};
|
||||
Ref<Point3> size()
|
||||
%code%{ RETVAL = &THIS->size; %};
|
||||
Clone<BoundingBox> bounding_box();
|
||||
@ -82,6 +85,8 @@ _constant()
|
||||
bool reload_model_instances();
|
||||
void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges)
|
||||
%code%{ THIS->layer_height_ranges = layer_height_ranges; %};
|
||||
void set_layer_height_profile(std::vector<double> profile)
|
||||
%code%{ THIS->layer_height_profile = profile; %};
|
||||
|
||||
size_t total_layer_count();
|
||||
size_t layer_count();
|
||||
@ -106,11 +111,32 @@ _constant()
|
||||
%code%{ THIS->state.set_done(step); %};
|
||||
void set_step_started(PrintObjectStep step)
|
||||
%code%{ THIS->state.set_started(step); %};
|
||||
|
||||
|
||||
void _slice();
|
||||
void detect_surfaces_type();
|
||||
void process_external_surfaces();
|
||||
void discover_vertical_shells();
|
||||
void bridge_over_infill();
|
||||
void _make_perimeters();
|
||||
void _infill();
|
||||
void _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; %};
|
||||
|
@ -1,26 +0,0 @@
|
||||
%module{Slic3r::XS};
|
||||
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/SupportMaterial.hpp"
|
||||
%}
|
||||
|
||||
%name{Slic3r::Print::SupportMaterial2} class PrintObjectSupportMaterial {
|
||||
PrintObjectSupportMaterial(PrintObject *print_object);
|
||||
~PrintObjectSupportMaterial();
|
||||
|
||||
void generate(PrintObject *object)
|
||||
%code{% THIS->generate(*object); %};
|
||||
};
|
||||
|
||||
%package{Slic3r::Print::SupportMaterial};
|
||||
%{
|
||||
|
||||
SV*
|
||||
MARGIN()
|
||||
PROTOTYPE:
|
||||
CODE:
|
||||
RETVAL = newSVnv(SUPPORT_MATERIAL_MARGIN);
|
||||
OUTPUT: RETVAL
|
||||
|
||||
%}
|
@ -83,13 +83,12 @@ Surface::polygons()
|
||||
RETVAL
|
||||
|
||||
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
|
||||
|
||||
|
@ -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:
|
||||
|
Loading…
Reference in New Issue
Block a user