Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEnrico Turri <enricoturri@seznam.cz>2018-07-17 11:44:23 +0300
committerEnrico Turri <enricoturri@seznam.cz>2018-07-17 11:44:23 +0300
commit5c907469146767a44d90d8760efcf78e555379c9 (patch)
treec7a273761920bf40f939f943dde1195b9c457313
parent8175c9d306b6e738788cd79334173b5f9b161662 (diff)
parent161a2bc6f863e922c4b62b7d630b63d0fb7b75ad (diff)
Merge with master + resolved conflicts
-rw-r--r--lib/Slic3r/GUI/3DScene.pm2163
-rw-r--r--lib/Slic3r/GUI/Plater.pm74
-rw-r--r--lib/Slic3r/GUI/Plater/3D.pm257
-rw-r--r--lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm17
-rw-r--r--resources/shaders/gouraud.vs8
-rw-r--r--resources/shaders/variable_layer_height.vs7
-rw-r--r--t/combineinfill.t1
-rw-r--r--t/fill.t1
-rw-r--r--xs/src/libslic3r/BoundingBox.cpp39
-rw-r--r--xs/src/libslic3r/BoundingBox.hpp2
-rw-r--r--xs/src/libslic3r/ExtrusionEntity.hpp4
-rw-r--r--xs/src/libslic3r/ExtrusionEntityCollection.cpp1
-rw-r--r--xs/src/libslic3r/ExtrusionEntityCollection.hpp1
-rw-r--r--xs/src/libslic3r/Format/AMF.cpp2
-rw-r--r--xs/src/libslic3r/GCode.cpp263
-rw-r--r--xs/src/libslic3r/GCode.hpp18
-rw-r--r--xs/src/libslic3r/GCode/ToolOrdering.cpp321
-rw-r--r--xs/src/libslic3r/GCode/ToolOrdering.hpp140
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp129
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp42
-rw-r--r--xs/src/libslic3r/Model.cpp40
-rw-r--r--xs/src/libslic3r/Model.hpp6
-rw-r--r--xs/src/libslic3r/Print.cpp114
-rw-r--r--xs/src/libslic3r/Print.hpp8
-rw-r--r--xs/src/libslic3r/PrintConfig.cpp55
-rw-r--r--xs/src/libslic3r/PrintConfig.hpp17
-rw-r--r--xs/src/libslic3r/PrintObject.cpp8
-rw-r--r--xs/src/libslic3r/Utils.hpp3
-rw-r--r--xs/src/libslic3r/utils.cpp24
-rw-r--r--xs/src/slic3r/GUI/3DScene.cpp406
-rw-r--r--xs/src/slic3r/GUI/3DScene.hpp64
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.cpp315
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.hpp24
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.cpp21
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.hpp3
-rw-r--r--xs/src/slic3r/GUI/GLGizmo.cpp68
-rw-r--r--xs/src/slic3r/GUI/GLGizmo.hpp15
-rw-r--r--xs/src/slic3r/GUI/GLShader.cpp11
-rw-r--r--xs/src/slic3r/GUI/GLShader.hpp1
-rw-r--r--xs/src/slic3r/GUI/GLTexture.cpp19
-rw-r--r--xs/src/slic3r/GUI/GUI.cpp2
-rw-r--r--xs/src/slic3r/GUI/Preset.cpp26
-rw-r--r--xs/src/slic3r/GUI/Preset.hpp4
-rw-r--r--xs/src/slic3r/GUI/PresetBundle.cpp22
-rw-r--r--xs/src/slic3r/GUI/Tab.cpp6
-rw-r--r--xs/xsp/GUI_3DScene.xsp28
46 files changed, 1703 insertions, 3097 deletions
diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index 157e7229c..23decaa37 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -16,102 +16,10 @@ use strict;
use warnings;
use Wx qw(wxTheApp :timer :bitmap :icon :dialog);
-#==============================================================================================================================
-#use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_CHAR EVT_TIMER);
# must load OpenGL *before* Wx::GLCanvas
use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
use base qw(Wx::GLCanvas Class::Accessor);
-#==============================================================================================================================
-#use Math::Trig qw(asin tan);
-#use List::Util qw(reduce min max first);
-#use Slic3r::Geometry qw(X Y normalize scale unscale scaled_epsilon);
-#use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl JT_ROUND);
-#==============================================================================================================================
use Wx::GLCanvas qw(:all);
-#==============================================================================================================================
-#use Slic3r::Geometry qw(PI);
-#==============================================================================================================================
-
-# volumes: reference to vector of Slic3r::GUI::3DScene::Volume.
-#==============================================================================================================================
-#__PACKAGE__->mk_accessors( qw(_quat _dirty init
-# enable_picking
-# enable_moving
-# use_plain_shader
-# on_viewport_changed
-# on_hover
-# on_select
-# on_double_click
-# on_right_click
-# on_move
-# on_model_update
-# volumes
-# _sphi _stheta
-# cutting_plane_z
-# cut_lines_vertices
-# bed_shape
-# bed_triangles
-# bed_grid_lines
-# bed_polygon
-# background
-# origin
-# _mouse_pos
-# _hover_volume_idx
-#
-# _drag_volume_idx
-# _drag_start_pos
-# _drag_volume_center_offset
-# _drag_start_xy
-# _dragged
-#
-# _layer_height_edited
-#
-# _camera_type
-# _camera_target
-# _camera_distance
-# _zoom
-#
-# _legend_enabled
-# _warning_enabled
-# _apply_zoom_to_volumes_filter
-# _mouse_dragging
-#
-# ) );
-#
-#use constant TRACKBALLSIZE => 0.8;
-#use constant TURNTABLE_MODE => 1;
-#use constant GROUND_Z => -0.02;
-## For mesh selection: Not selected - bright yellow.
-#use constant DEFAULT_COLOR => [1,1,0];
-## For mesh selection: Selected - bright green.
-#use constant SELECTED_COLOR => [0,1,0,1];
-## For mesh selection: Mouse hovers over the object, but object not selected yet - dark green.
-#use constant HOVER_COLOR => [0.4,0.9,0,1];
-#
-## phi / theta angles to orient the camera.
-#use constant VIEW_DEFAULT => [45.0,45.0];
-#use constant VIEW_LEFT => [90.0,90.0];
-#use constant VIEW_RIGHT => [-90.0,90.0];
-#use constant VIEW_TOP => [0.0,0.0];
-#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 MANIPULATION_IDLE => 0;
-#use constant MANIPULATION_DRAGGING => 1;
-#use constant MANIPULATION_LAYER_HEIGHT => 2;
-#
-#use constant GIMBALL_LOCK_THETA_MAX => 180;
-#
-#use constant VARIABLE_LAYER_THICKNESS_BAR_WIDTH => 70;
-#use constant VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT => 22;
-#
-## make OpenGL::Array thread-safe
-#{
-# no warnings 'redefine';
-# *OpenGL::Array::CLONE_SKIP = sub { 1 };
-#}
-#==============================================================================================================================
sub new {
my ($class, $parent) = @_;
@@ -135,2097 +43,28 @@ sub new {
# we request a depth buffer explicitely because it looks like it's not created by
# default on Linux, causing transparency issues
my $self = $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "", $attrib);
-#==============================================================================================================================
-# if (Wx::wxVERSION >= 3.000003) {
-# # Wx 3.0.3 contains an ugly hack to support some advanced OpenGL attributes through the attribute list.
-# # The attribute list is transferred between the wxGLCanvas and wxGLContext constructors using a single static array s_wglContextAttribs.
-# # Immediatelly force creation of the OpenGL context to consume the static variable s_wglContextAttribs.
-# $self->GetContext();
-# }
-#==============================================================================================================================
-#==============================================================================================================================
Slic3r::GUI::_3DScene::add_canvas($self);
Slic3r::GUI::_3DScene::allow_multisample($self, $can_multisample);
-# my $context = $self->GetContext;
-# $self->SetCurrent($context);
-# Slic3r::GUI::_3DScene::add_canvas($self, $context);
-#
-# $self->{can_multisample} = $can_multisample;
-# $self->background(1);
-# $self->_quat((0, 0, 0, 1));
-# $self->_stheta(45);
-# $self->_sphi(45);
-# $self->_zoom(1);
-# $self->_legend_enabled(0);
-# $self->_warning_enabled(0);
-# $self->use_plain_shader(0);
-# $self->_apply_zoom_to_volumes_filter(0);
-# $self->_mouse_dragging(0);
-#
-# # Collection of GLVolume objects
-# $self->volumes(Slic3r::GUI::_3DScene::GLVolume::Collection->new);
-#
-# # 3D point in model space
-# $self->_camera_type('ortho');
-## $self->_camera_type('perspective');
-# $self->_camera_target(Slic3r::Pointf3->new(0,0,0));
-# $self->_camera_distance(0.);
-# $self->layer_editing_enabled(0);
-# $self->{layer_height_edit_band_width} = 2.;
-# $self->{layer_height_edit_strength} = 0.005;
-# $self->{layer_height_edit_last_object_id} = -1;
-# $self->{layer_height_edit_last_z} = 0.;
-# $self->{layer_height_edit_last_action} = 0;
-#
-# $self->reset_objects;
-#
-# EVT_PAINT($self, sub {
-# my $dc = Wx::PaintDC->new($self);
-# $self->Render($dc);
-# });
-# EVT_SIZE($self, sub { $self->_dirty(1) });
-# EVT_IDLE($self, sub {
-# return unless $self->_dirty;
-# return if !$self->IsShownOnScreen;
-# $self->Resize( $self->GetSizeWH );
-# $self->Refresh;
-# });
-# EVT_MOUSEWHEEL($self, \&mouse_wheel_event);
-# EVT_MOUSE_EVENTS($self, \&mouse_event);
-## EVT_KEY_DOWN($self, sub {
-# EVT_CHAR($self, sub {
-# my ($s, $event) = @_;
-# if ($event->HasModifiers) {
-# $event->Skip;
-# } else {
-# my $key = $event->GetKeyCode;
-# if ($key == ord('0')) {
-# $self->select_view('iso');
-# } elsif ($key == ord('1')) {
-# $self->select_view('top');
-# } elsif ($key == ord('2')) {
-# $self->select_view('bottom');
-# } elsif ($key == ord('3')) {
-# $self->select_view('front');
-# } elsif ($key == ord('4')) {
-# $self->select_view('rear');
-# } elsif ($key == ord('5')) {
-# $self->select_view('left');
-# } elsif ($key == ord('6')) {
-# $self->select_view('right');
-# } elsif ($key == ord('z')) {
-# $self->zoom_to_volumes;
-# } elsif ($key == ord('b')) {
-# $self->zoom_to_bed;
-# } else {
-# $event->Skip;
-# }
-# }
-# });
-#
-# $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 != 1;
-# $self->_variable_layer_thickness_action(undef);
-# });
-#==============================================================================================================================
return $self;
}
-#==============================================================================================================================
-#sub set_legend_enabled {
-# my ($self, $value) = @_;
-# $self->_legend_enabled($value);
-#}
-#
-#sub set_warning_enabled {
-# my ($self, $value) = @_;
-# $self->_warning_enabled($value);
-#}
-#==============================================================================================================================
-
sub Destroy {
my ($self) = @_;
-#==============================================================================================================================
Slic3r::GUI::_3DScene::remove_canvas($self);
-# $self->{layer_height_edit_timer}->Stop;
-# $self->DestroyGL;
-#==============================================================================================================================
return $self->SUPER::Destroy;
}
-#==============================================================================================================================
-#sub layer_editing_enabled {
-# my ($self, $value) = @_;
-# if (@_ == 2) {
-# $self->{layer_editing_enabled} = $value;
-# if ($value) {
-# if (! $self->{layer_editing_initialized}) {
-# # Enabling the layer editing for the first time. This triggers compilation of the necessary OpenGL shaders.
-# # If compilation fails, a message box is shown with the error codes.
-# $self->SetCurrent($self->GetContext);
-# my $shader = new Slic3r::GUI::_3DScene::GLShader;
-# my $error_message;
-# if (! $shader->load_from_text($self->_fragment_shader_variable_layer_height, $self->_vertex_shader_variable_layer_height)) {
-# # Compilation or linking of the shaders failed.
-# $error_message = "Cannot compile an OpenGL Shader, therefore the Variable Layer Editing will be disabled.\n\n"
-# . $shader->last_error;
-# $shader = undef;
-# } else {
-# $self->{layer_height_edit_shader} = $shader;
-# ($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_WRAP_S, GL_CLAMP);
-# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
-# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
-# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
-# glBindTexture(GL_TEXTURE_2D, 0);
-# }
-# if (defined($error_message)) {
-# # Don't enable the layer editing tool.
-# $self->{layer_editing_enabled} = 0;
-# # 2 means failed
-# $self->{layer_editing_initialized} = 2;
-# # Show the error message.
-# Wx::MessageBox($error_message, "Slic3r Error", wxOK | wxICON_EXCLAMATION, $self);
-# } else {
-# $self->{layer_editing_initialized} = 1;
-# }
-# } elsif ($self->{layer_editing_initialized} == 2) {
-# # Initilization failed before. Don't try to initialize and disable layer editing.
-# $self->{layer_editing_enabled} = 0;
-# }
-# }
-# }
-# return $self->{layer_editing_enabled};
-#}
-#
-#sub layer_editing_allowed {
-# my ($self) = @_;
-# # Allow layer editing if either the shaders were not initialized yet and we don't know
-# # whether it will be possible to initialize them,
-# # or if the initialization was done already and it failed.
-# return ! (defined($self->{layer_editing_initialized}) && $self->{layer_editing_initialized} == 2);
-#}
-#
-#sub _first_selected_object_id_for_variable_layer_height_editing {
-# my ($self) = @_;
-# for my $i (0..$#{$self->volumes}) {
-# if ($self->volumes->[$i]->selected) {
-# my $object_id = int($self->volumes->[$i]->select_group_id / 1000000);
-# # Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy.
-# return ($object_id >= $self->{print}->object_count) ? -1 : $object_id
-# if $object_id < 10000;
-# }
-# }
-# 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_screen {
-# my ($self) = @_;
-# my ($cw, $ch) = $self->GetSizeWH;
-# return ($cw - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0, $cw, $ch - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT);
-#}
-#
-#sub _variable_layer_thickness_bar_rect_viewport {
-# my ($self) = @_;
-# my ($cw, $ch) = $self->GetSizeWH;
-# return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom, $cw/(2*$self->_zoom), $ch/(2*$self->_zoom));
-#}
-#
-## Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen.
-#sub _variable_layer_thickness_reset_rect_screen {
-# my ($self) = @_;
-# my ($cw, $ch) = $self->GetSizeWH;
-# return ($cw - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, $ch - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, $cw, $ch);
-#}
-#
-#sub _variable_layer_thickness_reset_rect_viewport {
-# my ($self) = @_;
-# my ($cw, $ch) = $self->GetSizeWH;
-# return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, -$ch/(2*$self->_zoom), $cw/(2*$self->_zoom), (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom);
-#}
-#
-#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_screen;
-# 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_reset_rect_mouse_inside {
-# my ($self, $mouse_evt) = @_;
-# my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_reset_rect_screen;
-# 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_relative {
-# my ($self) = @_;
-# my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition());
-# my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen;
-# return ($mouse_pos->x >= $bar_left && $mouse_pos->x <= $bar_right && $mouse_pos->y >= $bar_top && $mouse_pos->y <= $bar_bottom) ?
-# # Inside the bar.
-# ($bar_bottom - $mouse_pos->y - 1.) / ($bar_bottom - $bar_top - 1) :
-# # Outside the bar.
-# -1000.;
-#}
-#
-#sub _variable_layer_thickness_action {
-# my ($self, $mouse_event, $do_modification) = @_;
-# # A volume is selected. Test, whether hovering over a layer thickness bar.
-# return if $self->{layer_height_edit_last_object_id} == -1;
-# if (defined($mouse_event)) {
-# my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen;
-# $self->{layer_height_edit_last_z} = unscale($self->{print}->get_object($self->{layer_height_edit_last_object_id})->size->z)
-# * ($bar_bottom - $mouse_event->GetY - 1.) / ($bar_bottom - $bar_top);
-# $self->{layer_height_edit_last_action} = $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1);
-# }
-# # Mark the volume as modified, so Print will pick its layer height profile? Where to mark it?
-# # Start a timer to refresh the print? schedule_background_process() ?
-# # The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself.
-# $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->volumes->[$self->{layer_height_edit_last_object_id}]->generate_layer_height_texture(
-# $self->{print}->get_object($self->{layer_height_edit_last_object_id}), 1);
-# $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_for_variable_layer_height_editing : -1;
-#
-# $self->_mouse_dragging($e->Dragging);
-#
-# if ($e->Entering && (&Wx::wxMSW || $^O eq 'linux')) {
-# # wxMSW needs focus in order to catch mouse wheel events
-# $self->SetFocus;
-# $self->_drag_start_xy(undef);
-# } elsif ($e->LeftDClick) {
-# if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) {
-# } elsif ($self->on_double_click) {
-# $self->on_double_click->();
-# }
-# } elsif ($e->LeftDown || $e->RightDown) {
-# # If user pressed left or right button we first check whether this happened
-# # on a volume or not.
-# my $volume_idx = $self->_hover_volume_idx // -1;
-# $self->_layer_height_edited(0);
-# if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) {
-# # A volume is selected and the mouse is hovering over a layer thickness bar.
-# # Start editing the layer height.
-# $self->_layer_height_edited(1);
-# $self->_variable_layer_thickness_action($e);
-# } elsif ($object_idx_selected != -1 && $self->_variable_layer_thickness_reset_rect_mouse_inside($e)) {
-# $self->{print}->get_object($object_idx_selected)->reset_layer_height_profile;
-# # Index 2 means no editing, just wait for mouse up event.
-# $self->_layer_height_edited(2);
-# $self->Refresh;
-# $self->Update;
-# } else {
-# # The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate $pos->x,y,
-# # an converts the screen space coordinate to unscaled object space.
-# my $pos3d = ($volume_idx == -1) ? undef : $self->mouse_to_3d(@$pos);
-#
-# # 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;
-# $self->Update;
-# }
-#
-# # propagate event through callback
-# $self->on_select->($volume_idx)
-# if $self->on_select;
-#
-# if ($volume_idx != -1) {
-# if ($e->LeftDown && $self->enable_moving) {
-# # Only accept the initial position, if it is inside the volume bounding box.
-# my $volume_bbox = $self->volumes->[$volume_idx]->transformed_bounding_box;
-# $volume_bbox->offset(1.);
-# if ($volume_bbox->contains_point($pos3d)) {
-# # The dragging operation is initiated.
-# $self->_drag_volume_idx($volume_idx);
-# $self->_drag_start_pos($pos3d);
-# # Remember the shift to to the object center. The object center will later be used
-# # to limit the object placement close to the bed.
-# $self->_drag_volume_center_offset($pos3d->vector_to($volume_bbox->center));
-# }
-# } 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 && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) {
-# # Get new position at the same Z of the initial click point.
-# my $cur_pos = Slic3r::Linef3->new(
-# $self->mouse_to_3d($e->GetX, $e->GetY, 0),
-# $self->mouse_to_3d($e->GetX, $e->GetY, 1))
-# ->intersect_plane($self->_drag_start_pos->z);
-#
-# # Clip the new position, so the object center remains close to the bed.
-# {
-# $cur_pos->translate(@{$self->_drag_volume_center_offset});
-# my $cur_pos2 = Slic3r::Point->new(scale($cur_pos->x), scale($cur_pos->y));
-# if (! $self->bed_polygon->contains_point($cur_pos2)) {
-# my $ip = $self->bed_polygon->point_projection($cur_pos2);
-# $cur_pos->set_x(unscale($ip->x));
-# $cur_pos->set_y(unscale($ip->y));
-# }
-# $cur_pos->translate(@{$self->_drag_volume_center_offset->negative});
-# }
-# # Calculate the translation vector.
-# my $vector = $self->_drag_start_pos->vector_to($cur_pos);
-# # Get the volume being dragged.
-# my $volume = $self->volumes->[$self->_drag_volume_idx];
-# # Get all volumes belonging to the same group, if any.
-# my @volumes = ($volume->drag_group_id == -1) ?
-# ($volume) :
-# grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes};
-# # Apply new temporary volume origin and ignore Z.
-# $_->translate($vector->x, $vector->y, 0) for @volumes;
-# $self->_drag_start_pos($cur_pos);
-# $self->_dragged(1);
-# $self->Refresh;
-# $self->Update;
-# } elsif ($e->Dragging) {
-# if ($self->_layer_height_edited && $object_idx_selected != -1) {
-# $self->_variable_layer_thickness_action($e) if ($self->_layer_height_edited == 1);
-# } elsif ($e->LeftIsDown) {
-# # if dragging over blank area with left button, rotate
-# if (defined $self->_drag_start_pos) {
-# my $orig = $self->_drag_start_pos;
-# if (TURNTABLE_MODE) {
-# # Turntable mode is enabled by default.
-# $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE);
-# $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #-
-# $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX;
-# $self->_stheta(0) if $self->_stheta < 0;
-# } else {
-# my $size = $self->GetClientSize;
-# my @quat = trackball(
-# $orig->x / ($size->width / 2) - 1,
-# 1 - $orig->y / ($size->height / 2), #/
-# $pos->x / ($size->width / 2) - 1,
-# 1 - $pos->y / ($size->height / 2), #/
-# );
-# $self->_quat(mulquats($self->_quat, \@quat));
-# }
-# $self->on_viewport_changed->() if $self->on_viewport_changed;
-# $self->Refresh;
-# $self->Update;
-# }
-# $self->_drag_start_pos($pos);
-# } elsif ($e->MiddleIsDown || $e->RightIsDown) {
-# # If dragging over blank area with right button, pan.
-# if (defined $self->_drag_start_xy) {
-# # get point in model space at Z = 0
-# my $cur_pos = $self->mouse_to_3d($e->GetX, $e->GetY, 0);
-# my $orig = $self->mouse_to_3d($self->_drag_start_xy->x, $self->_drag_start_xy->y, 0);
-# $self->_camera_target->translate(@{$orig->vector_to($cur_pos)->negative});
-# $self->on_viewport_changed->() if $self->on_viewport_changed;
-# $self->Refresh;
-# $self->Update;
-# }
-# $self->_drag_start_xy($pos);
-# }
-# } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) {
-# if ($self->_layer_height_edited) {
-# $self->_layer_height_edited(undef);
-# $self->{layer_height_edit_timer}->Stop;
-# $self->on_model_update->()
-# if ($object_idx_selected != -1 && $self->on_model_update);
-# } elsif ($self->on_move && defined($self->_drag_volume_idx) && $self->_dragged) {
-# # get all volumes belonging to the same group, if any
-# my @volume_idxs;
-# my $group_id = $self->volumes->[$self->_drag_volume_idx]->drag_group_id;
-# if ($group_id == -1) {
-# @volume_idxs = ($self->_drag_volume_idx);
-# } else {
-# @volume_idxs = grep $self->volumes->[$_]->drag_group_id == $group_id,
-# 0..$#{$self->volumes};
-# }
-# $self->on_move->(@volume_idxs);
-# }
-# $self->_drag_volume_idx(undef);
-# $self->_drag_start_pos(undef);
-# $self->_drag_start_xy(undef);
-# $self->_dragged(undef);
-# } elsif ($e->Moving) {
-# $self->_mouse_pos($pos);
-# # Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor
-# # hovers over.
-# if ($self->enable_picking) {
-# $self->Update;
-# $self->Refresh;
-# }
-# } else {
-# $e->Skip();
-# }
-#}
-#
-#sub mouse_wheel_event {
-# my ($self, $e) = @_;
-#
-# if ($e->MiddleIsDown) {
-# # Ignore the wheel events if the middle button is pressed.
-# return;
-# }
-# if ($self->layer_editing_enabled && $self->{print}) {
-# my $object_idx_selected = $self->_first_selected_object_id_for_variable_layer_height_editing;
-# 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;
-# $zoom = $self->_zoom / (1-$zoom);
-# # Don't allow to zoom too far outside the scene.
-# my $zoom_min = $self->get_zoom_to_bounding_box_factor($self->max_bounding_box);
-# $zoom_min *= 0.4 if defined $zoom_min;
-# $zoom = $zoom_min if defined $zoom_min && $zoom < $zoom_min;
-# $self->_zoom($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->Resize($self->GetSizeWH) if $self->IsShownOnScreen;
-# $self->Refresh;
-#}
-#
-## Reset selection.
-#sub reset_objects {
-# my ($self) = @_;
-# if ($self->GetContext) {
-# $self->SetCurrent($self->GetContext);
-# $self->volumes->release_geometry;
-# }
-# $self->volumes->erase;
-# $self->_dirty(1);
-#}
-#
-## Setup camera to view all objects.
-#sub set_viewport_from_scene {
-# my ($self, $scene) = @_;
-#
-# $self->_sphi($scene->_sphi);
-# $self->_stheta($scene->_stheta);
-# $self->_camera_target($scene->_camera_target);
-# $self->_zoom($scene->_zoom);
-# $self->_quat($scene->_quat);
-# $self->_dirty(1);
-#}
-#
-## Set the camera to a default orientation,
-## zoom to volumes.
-#sub select_view {
-# my ($self, $direction) = @_;
-#
-# my $dirvec;
-# if (ref($direction)) {
-# $dirvec = $direction;
-# } else {
-# if ($direction eq 'iso') {
-# $dirvec = VIEW_DEFAULT;
-# } elsif ($direction eq 'left') {
-# $dirvec = VIEW_LEFT;
-# } elsif ($direction eq 'right') {
-# $dirvec = VIEW_RIGHT;
-# } elsif ($direction eq 'top') {
-# $dirvec = VIEW_TOP;
-# } elsif ($direction eq 'bottom') {
-# $dirvec = VIEW_BOTTOM;
-# } elsif ($direction eq 'front') {
-# $dirvec = VIEW_FRONT;
-# } elsif ($direction eq 'rear') {
-# $dirvec = VIEW_REAR;
-# }
-# }
-# my $bb = $self->volumes_bounding_box;
-# if (! $bb->empty) {
-# $self->_sphi($dirvec->[0]);
-# $self->_stheta($dirvec->[1]);
-# # Avoid gimball lock.
-# $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX;
-# $self->_stheta(0) if $self->_stheta < 0;
-# $self->on_viewport_changed->() if $self->on_viewport_changed;
-# $self->Refresh;
-# }
-#}
-#
-#sub get_zoom_to_bounding_box_factor {
-# my ($self, $bb) = @_;
-# my $max_bb_size = max(@{ $bb->size });
-# return undef if ($max_bb_size == 0);
-#
-# # project the bbox vertices on a plane perpendicular to the camera forward axis
-# # then calculates the vertices coordinate on this plane along the camera xy axes
-#
-# # we need the view matrix, we let opengl calculate it (same as done in render sub)
-# glMatrixMode(GL_MODELVIEW);
-# glLoadIdentity();
-#
-# if (!TURNTABLE_MODE) {
-# # Shift the perspective camera.
-# my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance);
-# glTranslatef(@$camera_pos);
-# }
-#
-# if (TURNTABLE_MODE) {
-# # Turntable mode is enabled by default.
-# glRotatef(-$self->_stheta, 1, 0, 0); # pitch
-# glRotatef($self->_sphi, 0, 0, 1); # yaw
-# } else {
-# # Shift the perspective camera.
-# my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance);
-# glTranslatef(@$camera_pos);
-# my @rotmat = quat_to_rotmatrix($self->quat);
-# glMultMatrixd_p(@rotmat[0..15]);
-# }
-# glTranslatef(@{ $self->_camera_target->negative });
-#
-# # get the view matrix back from opengl
-# my @matrix = glGetFloatv_p(GL_MODELVIEW_MATRIX);
-#
-# # camera axes
-# my $right = Slic3r::Pointf3->new($matrix[0], $matrix[4], $matrix[8]);
-# my $up = Slic3r::Pointf3->new($matrix[1], $matrix[5], $matrix[9]);
-# my $forward = Slic3r::Pointf3->new($matrix[2], $matrix[6], $matrix[10]);
-#
-# my $bb_min = $bb->min_point();
-# my $bb_max = $bb->max_point();
-# my $bb_center = $bb->center();
-#
-# # bbox vertices in world space
-# my @vertices = ();
-# push(@vertices, $bb_min);
-# push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_min->y(), $bb_min->z()));
-# push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_max->y(), $bb_min->z()));
-# push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_max->y(), $bb_min->z()));
-# push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_min->y(), $bb_max->z()));
-# push(@vertices, Slic3r::Pointf3->new($bb_max->x(), $bb_min->y(), $bb_max->z()));
-# push(@vertices, $bb_max);
-# push(@vertices, Slic3r::Pointf3->new($bb_min->x(), $bb_max->y(), $bb_max->z()));
-#
-# my $max_x = 0.0;
-# my $max_y = 0.0;
-#
-# # margin factor to give some empty space around the bbox
-# my $margin_factor = 1.25;
-#
-# foreach my $v (@vertices) {
-# # project vertex on the plane perpendicular to camera forward axis
-# my $pos = Slic3r::Pointf3->new($v->x() - $bb_center->x(), $v->y() - $bb_center->y(), $v->z() - $bb_center->z());
-# my $proj_on_normal = $pos->x() * $forward->x() + $pos->y() * $forward->y() + $pos->z() * $forward->z();
-# my $proj_on_plane = Slic3r::Pointf3->new($pos->x() - $proj_on_normal * $forward->x(), $pos->y() - $proj_on_normal * $forward->y(), $pos->z() - $proj_on_normal * $forward->z());
-#
-# # calculates vertex coordinate along camera xy axes
-# my $x_on_plane = $proj_on_plane->x() * $right->x() + $proj_on_plane->y() * $right->y() + $proj_on_plane->z() * $right->z();
-# my $y_on_plane = $proj_on_plane->x() * $up->x() + $proj_on_plane->y() * $up->y() + $proj_on_plane->z() * $up->z();
-#
-# $max_x = max($max_x, $margin_factor * 2 * abs($x_on_plane));
-# $max_y = max($max_y, $margin_factor * 2 * abs($y_on_plane));
-# }
-#
-# return undef if (($max_x == 0) || ($max_y == 0));
-#
-# my ($cw, $ch) = $self->GetSizeWH;
-# my $min_ratio = min($cw / $max_x, $ch / $max_y);
-#
-# return $min_ratio;
-#}
-#
-#sub zoom_to_bounding_box {
-# my ($self, $bb) = @_;
-# # Calculate the zoom factor needed to adjust viewport to bounding box.
-# my $zoom = $self->get_zoom_to_bounding_box_factor($bb);
-# if (defined $zoom) {
-# $self->_zoom($zoom);
-# # center view around bounding box center
-# $self->_camera_target($bb->center);
-# $self->on_viewport_changed->() if $self->on_viewport_changed;
-# $self->Resize($self->GetSizeWH) if $self->IsShownOnScreen;
-# $self->Refresh;
-# }
-#}
-#
-#sub zoom_to_bed {
-# my ($self) = @_;
-#
-# if ($self->bed_shape) {
-# $self->zoom_to_bounding_box($self->bed_bounding_box);
-# }
-#}
-#
-#sub zoom_to_volume {
-# my ($self, $volume_idx) = @_;
-#
-# my $volume = $self->volumes->[$volume_idx];
-# my $bb = $volume->transformed_bounding_box;
-# $self->zoom_to_bounding_box($bb);
-#}
-#
-#sub zoom_to_volumes {
-# my ($self) = @_;
-#
-# $self->_apply_zoom_to_volumes_filter(1);
-# $self->zoom_to_bounding_box($self->volumes_bounding_box);
-# $self->_apply_zoom_to_volumes_filter(0);
-#}
-#
-#sub volumes_bounding_box {
-# my ($self) = @_;
-#
-# my $bb = Slic3r::Geometry::BoundingBoxf3->new;
-# foreach my $v (@{$self->volumes}) {
-# $bb->merge($v->transformed_bounding_box) if (! $self->_apply_zoom_to_volumes_filter || $v->zoom_to_volumes);
-# }
-# return $bb;
-#}
-#
-#sub bed_bounding_box {
-# my ($self) = @_;
-#
-# my $bb = Slic3r::Geometry::BoundingBoxf3->new;
-# if ($self->bed_shape) {
-# $bb->merge_point(Slic3r::Pointf3->new(@$_, 0)) for @{$self->bed_shape};
-# }
-# return $bb;
-#}
-#
-#sub max_bounding_box {
-# my ($self) = @_;
-#
-# my $bb = $self->bed_bounding_box;
-# $bb->merge($self->volumes_bounding_box);
-# return $bb;
-#}
-#
-## Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane
-## to support the scene objects.
-#sub set_auto_bed_shape {
-# my ($self, $bed_shape) = @_;
-#
-# # draw a default square bed around object center
-# my $max_size = max(@{ $self->volumes_bounding_box->size });
-# my $center = $self->volumes_bounding_box->center;
-# $self->set_bed_shape([
-# [ $center->x - $max_size, $center->y - $max_size ], #--
-# [ $center->x + $max_size, $center->y - $max_size ], #--
-# [ $center->x + $max_size, $center->y + $max_size ], #++
-# [ $center->x - $max_size, $center->y + $max_size ], #++
-# ]);
-# # Set the origin for painting of the coordinate system axes.
-# $self->origin(Slic3r::Pointf->new(@$center[X,Y]));
-#}
-#
-## Set the bed shape to a single closed 2D polygon (array of two element arrays),
-## triangulate the bed and store the triangles into $self->bed_triangles,
-## fills the $self->bed_grid_lines and sets $self->origin.
-## Sets $self->bed_polygon to limit the object placement.
-#sub set_bed_shape {
-# my ($self, $bed_shape) = @_;
-#
-# $self->bed_shape($bed_shape);
-#
-# # triangulate bed
-# my $expolygon = Slic3r::ExPolygon->new([ map [map scale($_), @$_], @$bed_shape ]);
-# my $bed_bb = $expolygon->bounding_box;
-#
-# {
-# my @points = ();
-# foreach my $triangle (@{ $expolygon->triangulate }) {
-# push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$triangle;
-# }
-# $self->bed_triangles(OpenGL::Array->new_list(GL_FLOAT, @points));
-# }
-#
-# {
-# my @polylines = ();
-# for (my $x = $bed_bb->x_min; $x <= $bed_bb->x_max; $x += scale 10) {
-# push @polylines, Slic3r::Polyline->new([$x,$bed_bb->y_min], [$x,$bed_bb->y_max]);
-# }
-# for (my $y = $bed_bb->y_min; $y <= $bed_bb->y_max; $y += scale 10) {
-# push @polylines, Slic3r::Polyline->new([$bed_bb->x_min,$y], [$bed_bb->x_max,$y]);
-# }
-# # clip with a slightly grown expolygon because our lines lay on the contours and
-# # may get erroneously clipped
-# my @lines = map Slic3r::Line->new(@$_[0,-1]),
-# @{intersection_pl(\@polylines, [ @{$expolygon->offset(+scaled_epsilon)} ])};
-#
-# # append bed contours
-# push @lines, map @{$_->lines}, @$expolygon;
-#
-# my @points = ();
-# foreach my $line (@lines) {
-# push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$line; #))
-# }
-# $self->bed_grid_lines(OpenGL::Array->new_list(GL_FLOAT, @points));
-# }
-#
-# # Set the origin for painting of the coordinate system axes.
-# $self->origin(Slic3r::Pointf->new(0,0));
-#
-# $self->bed_polygon(offset_ex([$expolygon->contour], $bed_bb->radius * 1.7, JT_ROUND, scale(0.5))->[0]->contour->clone);
-#}
-#
-#sub deselect_volumes {
-# my ($self) = @_;
-# $_->set_selected(0) for @{$self->volumes};
-#}
-#
-#sub select_volume {
-# my ($self, $volume_idx) = @_;
-#
-# return if ($volume_idx >= scalar(@{$self->volumes}));
-#
-# $self->volumes->[$volume_idx]->set_selected(1)
-# if $volume_idx != -1;
-#}
-#
-#sub SetCuttingPlane {
-# my ($self, $z, $expolygons) = @_;
-#
-# $self->cutting_plane_z($z);
-#
-# # grow slices in order to display them better
-# $expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1);
-#
-# my @verts = ();
-# foreach my $line (map @{$_->lines}, map @$_, @$expolygons) {
-# push @verts, (
-# unscale($line->a->x), unscale($line->a->y), $z, #))
-# unscale($line->b->x), unscale($line->b->y), $z, #))
-# );
-# }
-# $self->cut_lines_vertices(OpenGL::Array->new_list(GL_FLOAT, @verts));
-#}
-#
-## Given an axis and angle, compute quaternion.
-#sub axis_to_quat {
-# my ($ax, $phi) = @_;
-#
-# my $lena = sqrt(reduce { $a + $b } (map { $_ * $_ } @$ax));
-# my @q = map { $_ * (1 / $lena) } @$ax;
-# @q = map { $_ * sin($phi / 2.0) } @q;
-# $q[$#q + 1] = cos($phi / 2.0);
-# return @q;
-#}
-#
-## Project a point on the virtual trackball.
-## If it is inside the sphere, map it to the sphere, if it outside map it
-## to a hyperbola.
-#sub project_to_sphere {
-# my ($r, $x, $y) = @_;
-#
-# my $d = sqrt($x * $x + $y * $y);
-# if ($d < $r * 0.70710678118654752440) { # Inside sphere
-# return sqrt($r * $r - $d * $d);
-# } else { # On hyperbola
-# my $t = $r / 1.41421356237309504880;
-# return $t * $t / $d;
-# }
-#}
-#
-#sub cross {
-# my ($v1, $v2) = @_;
-#
-# return (@$v1[1] * @$v2[2] - @$v1[2] * @$v2[1],
-# @$v1[2] * @$v2[0] - @$v1[0] * @$v2[2],
-# @$v1[0] * @$v2[1] - @$v1[1] * @$v2[0]);
-#}
-#
-## Simulate a track-ball. Project the points onto the virtual trackball,
-## then figure out the axis of rotation, which is the cross product of
-## P1 P2 and O P1 (O is the center of the ball, 0,0,0) Note: This is a
-## deformed trackball-- is a trackball in the center, but is deformed
-## into a hyperbolic sheet of rotation away from the center.
-## It is assumed that the arguments to this routine are in the range
-## (-1.0 ... 1.0).
-#sub trackball {
-# my ($p1x, $p1y, $p2x, $p2y) = @_;
-#
-# if ($p1x == $p2x && $p1y == $p2y) {
-# # zero rotation
-# return (0.0, 0.0, 0.0, 1.0);
-# }
-#
-# # First, figure out z-coordinates for projection of P1 and P2 to
-# # deformed sphere
-# my @p1 = ($p1x, $p1y, project_to_sphere(TRACKBALLSIZE, $p1x, $p1y));
-# my @p2 = ($p2x, $p2y, project_to_sphere(TRACKBALLSIZE, $p2x, $p2y));
-#
-# # axis of rotation (cross product of P1 and P2)
-# my @a = cross(\@p2, \@p1);
-#
-# # Figure out how much to rotate around that axis.
-# my @d = map { $_ * $_ } (map { $p1[$_] - $p2[$_] } 0 .. $#p1);
-# my $t = sqrt(reduce { $a + $b } @d) / (2.0 * TRACKBALLSIZE);
-#
-# # Avoid problems with out-of-control values...
-# $t = 1.0 if ($t > 1.0);
-# $t = -1.0 if ($t < -1.0);
-# my $phi = 2.0 * asin($t);
-#
-# return axis_to_quat(\@a, $phi);
-#}
-#
-## Build a rotation matrix, given a quaternion rotation.
-#sub quat_to_rotmatrix {
-# my ($q) = @_;
-#
-# my @m = ();
-#
-# $m[0] = 1.0 - 2.0 * (@$q[1] * @$q[1] + @$q[2] * @$q[2]);
-# $m[1] = 2.0 * (@$q[0] * @$q[1] - @$q[2] * @$q[3]);
-# $m[2] = 2.0 * (@$q[2] * @$q[0] + @$q[1] * @$q[3]);
-# $m[3] = 0.0;
-#
-# $m[4] = 2.0 * (@$q[0] * @$q[1] + @$q[2] * @$q[3]);
-# $m[5] = 1.0 - 2.0 * (@$q[2] * @$q[2] + @$q[0] * @$q[0]);
-# $m[6] = 2.0 * (@$q[1] * @$q[2] - @$q[0] * @$q[3]);
-# $m[7] = 0.0;
-#
-# $m[8] = 2.0 * (@$q[2] * @$q[0] - @$q[1] * @$q[3]);
-# $m[9] = 2.0 * (@$q[1] * @$q[2] + @$q[0] * @$q[3]);
-# $m[10] = 1.0 - 2.0 * (@$q[1] * @$q[1] + @$q[0] * @$q[0]);
-# $m[11] = 0.0;
-#
-# $m[12] = 0.0;
-# $m[13] = 0.0;
-# $m[14] = 0.0;
-# $m[15] = 1.0;
-#
-# return @m;
-#}
-#
-#sub mulquats {
-# my ($q1, $rq) = @_;
-#
-# return (@$q1[3] * @$rq[0] + @$q1[0] * @$rq[3] + @$q1[1] * @$rq[2] - @$q1[2] * @$rq[1],
-# @$q1[3] * @$rq[1] + @$q1[1] * @$rq[3] + @$q1[2] * @$rq[0] - @$q1[0] * @$rq[2],
-# @$q1[3] * @$rq[2] + @$q1[2] * @$rq[3] + @$q1[0] * @$rq[1] - @$q1[1] * @$rq[0],
-# @$q1[3] * @$rq[3] - @$q1[0] * @$rq[0] - @$q1[1] * @$rq[1] - @$q1[2] * @$rq[2])
-#}
-#
-## Convert the screen space coordinate to an object space coordinate.
-## If the Z screen space coordinate is not provided, a depth buffer value is substituted.
-#sub mouse_to_3d {
-# my ($self, $x, $y, $z) = @_;
-#
-# return unless $self->GetContext;
-# $self->SetCurrent($self->GetContext);
-#
-# my @viewport = glGetIntegerv_p(GL_VIEWPORT); # 4 items
-# my @mview = glGetDoublev_p(GL_MODELVIEW_MATRIX); # 16 items
-# my @proj = glGetDoublev_p(GL_PROJECTION_MATRIX); # 16 items
-#
-# $y = $viewport[3] - $y;
-# $z //= glReadPixels_p($x, $y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT);
-# my @projected = gluUnProject_p($x, $y, $z, @mview, @proj, @viewport);
-# return Slic3r::Pointf3->new(@projected);
-#}
-#
-#sub GetContext {
-# my ($self) = @_;
-# return $self->{context} ||= Wx::GLContext->new($self);
-#}
-#
-#sub SetCurrent {
-# my ($self, $context) = @_;
-# return $self->SUPER::SetCurrent($context);
-#}
-#
-#sub UseVBOs {
-# my ($self) = @_;
-#
-# if (! defined ($self->{use_VBOs})) {
-# my $use_legacy = wxTheApp->{app_config}->get('use_legacy_opengl');
-# if ($use_legacy eq '1') {
-# # Disable OpenGL 2.0 rendering.
-# $self->{use_VBOs} = 0;
-# # Don't enable the layer editing tool.
-# $self->{layer_editing_enabled} = 0;
-# # 2 means failed
-# $self->{layer_editing_initialized} = 2;
-# return 0;
-# }
-# # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized
-# # first when an OpenGL widget is shown for the first time. How ugly.
-# return 0 if (! $self->init && $^O eq 'linux');
-# # Don't use VBOs if anything fails.
-# $self->{use_VBOs} = 0;
-# if ($self->GetContext) {
-# $self->SetCurrent($self->GetContext);
-# Slic3r::GUI::_3DScene::_glew_init;
-# my @gl_version = split(/\./, glGetString(GL_VERSION));
-# $self->{use_VBOs} = int($gl_version[0]) >= 2;
-# # print "UseVBOs $self OpenGL major: $gl_version[0], minor: $gl_version[1]. Use VBOs: ", $self->{use_VBOs}, "\n";
-# }
-# }
-# return $self->{use_VBOs};
-#}
-#
-#sub Resize {
-# my ($self, $x, $y) = @_;
-#
-# return unless $self->GetContext;
-# $self->_dirty(0);
-#
-# $self->SetCurrent($self->GetContext);
-# glViewport(0, 0, $x, $y);
-#
-# $x /= $self->_zoom;
-# $y /= $self->_zoom;
-#
-# glMatrixMode(GL_PROJECTION);
-# glLoadIdentity();
-# if ($self->_camera_type eq 'ortho') {
-# #FIXME setting the size of the box 10x larger than necessary
-# # is only a workaround for an incorrectly set camera.
-# # This workaround harms Z-buffer accuracy!
-## my $depth = 1.05 * $self->max_bounding_box->radius();
-# my $depth = 5.0 * max(@{ $self->max_bounding_box->size });
-# glOrtho(
-# -$x/2, $x/2, -$y/2, $y/2,
-# -$depth, $depth,
-# );
-# } else {
-# die "Invalid camera type: ", $self->_camera_type, "\n" if ($self->_camera_type ne 'perspective');
-# my $bbox_r = $self->max_bounding_box->radius();
-# my $fov = PI * 45. / 180.;
-# my $fov_tan = tan(0.5 * $fov);
-# my $cam_distance = 0.5 * $bbox_r / $fov_tan;
-# $self->_camera_distance($cam_distance);
-# my $nr = $cam_distance - $bbox_r * 1.1;
-# my $fr = $cam_distance + $bbox_r * 1.1;
-# $nr = 1 if ($nr < 1);
-# $fr = $nr + 1 if ($fr < $nr + 1);
-# my $h2 = $fov_tan * $nr;
-# my $w2 = $h2 * $x / $y;
-# glFrustum(-$w2, $w2, -$h2, $h2, $nr, $fr);
-# }
-# glMatrixMode(GL_MODELVIEW);
-#}
-#
-#sub InitGL {
-# my $self = shift;
-#
-# return if $self->init;
-# return unless $self->GetContext;
-# $self->init(1);
-#
-## # This is a special path for wxWidgets on GTK, where an OpenGL context is initialized
-## # first when an OpenGL widget is shown for the first time. How ugly.
-## # In that case the volumes are wainting to be moved to Vertex Buffer Objects
-## # after the OpenGL context is being initialized.
-## $self->volumes->finalize_geometry(1)
-## if ($^O eq 'linux' && $self->UseVBOs);
-#
-# $self->zoom_to_bed;
-#
-# glClearColor(0, 0, 0, 1);
-# glColor3f(1, 0, 0);
-# glEnable(GL_DEPTH_TEST);
-# glClearDepth(1.0);
-# glDepthFunc(GL_LEQUAL);
-# glEnable(GL_CULL_FACE);
-# glEnable(GL_BLEND);
-# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-#
-# # Set antialiasing/multisampling
-# glDisable(GL_LINE_SMOOTH);
-# glDisable(GL_POLYGON_SMOOTH);
-#
-# # See "GL_MULTISAMPLE and GL_ARRAY_BUFFER_ARB messages on failed launch"
-# # https://github.com/alexrj/Slic3r/issues/4085
-# eval {
-# # Disable the multi sampling by default, so the picking by color will work correctly.
-# glDisable(GL_MULTISAMPLE);
-# };
-# # Disable multi sampling if the eval failed.
-# $self->{can_multisample} = 0 if $@;
-#
-# # ambient lighting
-# glLightModelfv_p(GL_LIGHT_MODEL_AMBIENT, 0.3, 0.3, 0.3, 1);
-#
-# glEnable(GL_LIGHTING);
-# glEnable(GL_LIGHT0);
-# glEnable(GL_LIGHT1);
-#
-# # light from camera
-# glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0);
-# glLightfv_p(GL_LIGHT1, GL_SPECULAR, 0.3, 0.3, 0.3, 1);
-# glLightfv_p(GL_LIGHT1, GL_DIFFUSE, 0.2, 0.2, 0.2, 1);
-#
-# # Enables Smooth Color Shading; try GL_FLAT for (lack of) fun.
-# glShadeModel(GL_SMOOTH);
-#
-## glMaterialfv_p(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, 0.5, 0.3, 0.3, 1);
-## glMaterialfv_p(GL_FRONT_AND_BACK, GL_SPECULAR, 1, 1, 1, 1);
-## glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50);
-## glMaterialfv_p(GL_FRONT_AND_BACK, GL_EMISSION, 0.1, 0, 0, 0.9);
-#
-# # A handy trick -- have surface material mirror the color.
-# glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
-# glEnable(GL_COLOR_MATERIAL);
-# glEnable(GL_MULTISAMPLE) if ($self->{can_multisample});
-#
-# if ($self->UseVBOs) {
-# my $shader = new Slic3r::GUI::_3DScene::GLShader;
-## if (! $shader->load($self->_fragment_shader_Phong, $self->_vertex_shader_Phong)) {
-# print "Compilaton of path shader failed: \n" . $shader->last_error . "\n";
-# $shader = undef;
-# } else {
-# $self->{plain_shader} = $shader;
-# }
-# }
-#}
-#
-#sub DestroyGL {
-# my $self = shift;
-# if ($self->GetContext) {
-# $self->SetCurrent($self->GetContext);
-# if ($self->{plain_shader}) {
-# $self->{plain_shader}->release;
-# delete $self->{plain_shader};
-# }
-# if ($self->{layer_height_edit_shader}) {
-# $self->{layer_height_edit_shader}->release;
-# delete $self->{layer_height_edit_shader};
-# }
-# $self->volumes->release_geometry;
-# }
-#}
-#
-#sub Render {
-# my ($self, $dc) = @_;
-#
-# # prevent calling SetCurrent() when window is not shown yet
-# return unless $self->IsShownOnScreen;
-# return unless my $context = $self->GetContext;
-# $self->SetCurrent($context);
-# $self->InitGL;
-#
-# glClearColor(1, 1, 1, 1);
-# glClearDepth(1);
-# glDepthFunc(GL_LESS);
-# glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-#
-# glMatrixMode(GL_MODELVIEW);
-# glLoadIdentity();
-#
-# if (!TURNTABLE_MODE) {
-# # Shift the perspective camera.
-# my $camera_pos = Slic3r::Pointf3->new(0,0,-$self->_camera_distance);
-# glTranslatef(@$camera_pos);
-# }
-#
-# if (TURNTABLE_MODE) {
-# # Turntable mode is enabled by default.
-# glRotatef(-$self->_stheta, 1, 0, 0); # pitch
-# glRotatef($self->_sphi, 0, 0, 1); # yaw
-# } else {
-# my @rotmat = quat_to_rotmatrix($self->quat);
-# glMultMatrixd_p(@rotmat[0..15]);
-# }
-#
-# glTranslatef(@{ $self->_camera_target->negative });
-#
-# # light from above
-# 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 && !$self->_mouse_dragging) {
-# if (my $pos = $self->_mouse_pos) {
-# # 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.
-# glPushAttrib(GL_ENABLE_BIT);
-# glDisable(GL_MULTISAMPLE) if ($self->{can_multisample});
-# glDisable(GL_LIGHTING);
-# glDisable(GL_BLEND);
-# $self->draw_volumes(1);
-# glPopAttrib();
-# glFlush();
-# my $col = [ glReadPixels_p($pos->x, $self->GetSize->GetHeight - $pos->y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE) ];
-# my $volume_idx = $col->[0] + $col->[1]*256 + $col->[2]*256*256;
-# $self->_hover_volume_idx(undef);
-# $_->set_hover(0) for @{$self->volumes};
-# if ($volume_idx <= $#{$self->volumes}) {
-# $self->_hover_volume_idx($volume_idx);
-#
-# $self->volumes->[$volume_idx]->set_hover(1);
-# my $group_id = $self->volumes->[$volume_idx]->select_group_id;
-# if ($group_id != -1) {
-# $_->set_hover(1) for grep { $_->select_group_id == $group_id } @{$self->volumes};
-# }
-#
-# $self->on_hover->($volume_idx) if $self->on_hover;
-# }
-# glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-# }
-# }
-#
-# # draw fixed background
-# if ($self->background) {
-# glDisable(GL_LIGHTING);
-# glPushMatrix();
-# glLoadIdentity();
-#
-# glMatrixMode(GL_PROJECTION);
-# glPushMatrix();
-# glLoadIdentity();
-#
-# # Draws a bluish bottom to top gradient over the complete screen.
-# glDisable(GL_DEPTH_TEST);
-# glBegin(GL_QUADS);
-# glColor3f(0.0,0.0,0.0);
-# glVertex3f(-1.0,-1.0, 1.0);
-# glVertex3f( 1.0,-1.0, 1.0);
-# glColor3f(10/255,98/255,144/255);
-# glVertex3f( 1.0, 1.0, 1.0);
-# glVertex3f(-1.0, 1.0, 1.0);
-# glEnd();
-# glPopMatrix();
-# glEnable(GL_DEPTH_TEST);
-#
-# glMatrixMode(GL_MODELVIEW);
-# glPopMatrix();
-# glEnable(GL_LIGHTING);
-# }
-#
-# # draw ground and axes
-# glDisable(GL_LIGHTING);
-#
-# # draw ground
-# my $ground_z = GROUND_Z;
-#
-# if ($self->bed_triangles) {
-# glDisable(GL_DEPTH_TEST);
-#
-# glEnable(GL_BLEND);
-# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-#
-# glEnableClientState(GL_VERTEX_ARRAY);
-# glColor4f(0.8, 0.6, 0.5, 0.4);
-# glNormal3d(0,0,1);
-# glVertexPointer_c(3, GL_FLOAT, 0, $self->bed_triangles->ptr());
-# glDrawArrays(GL_TRIANGLES, 0, $self->bed_triangles->elements / 3);
-# glDisableClientState(GL_VERTEX_ARRAY);
-#
-# # we need depth test for grid, otherwise it would disappear when looking
-# # the object from below
-# glEnable(GL_DEPTH_TEST);
-#
-# # draw grid
-# glLineWidth(3);
-# glColor4f(0.2, 0.2, 0.2, 0.4);
-# glEnableClientState(GL_VERTEX_ARRAY);
-# glVertexPointer_c(3, GL_FLOAT, 0, $self->bed_grid_lines->ptr());
-# glDrawArrays(GL_LINES, 0, $self->bed_grid_lines->elements / 3);
-# glDisableClientState(GL_VERTEX_ARRAY);
-#
-# glDisable(GL_BLEND);
-# }
-#
-# my $volumes_bb = $self->volumes_bounding_box;
-#
-# {
-# # draw axes
-# # disable depth testing so that axes are not covered by ground
-# glDisable(GL_DEPTH_TEST);
-# my $origin = $self->origin;
-# my $axis_len = $self->use_plain_shader ? 0.3 * max(@{ $self->bed_bounding_box->size }) : 2 * max(@{ $volumes_bb->size });
-# glLineWidth(2);
-# glBegin(GL_LINES);
-# # draw line for x axis
-# glColor3f(1, 0, 0);
-# glVertex3f(@$origin, $ground_z);
-# glVertex3f($origin->x + $axis_len, $origin->y, $ground_z); #,,
-# # draw line for y axis
-# glColor3f(0, 1, 0);
-# glVertex3f(@$origin, $ground_z);
-# glVertex3f($origin->x, $origin->y + $axis_len, $ground_z); #++
-# glEnd();
-# # draw line for Z axis
-# # (re-enable depth test so that axis is correctly shown when objects are behind it)
-# glEnable(GL_DEPTH_TEST);
-# glBegin(GL_LINES);
-# glColor3f(0, 0, 1);
-# glVertex3f(@$origin, $ground_z);
-# glVertex3f(@$origin, $ground_z+$axis_len);
-# glEnd();
-# }
-#
-# glEnable(GL_LIGHTING);
-#
-# # draw objects
-# if (! $self->use_plain_shader) {
-# $self->draw_volumes;
-# } elsif ($self->UseVBOs) {
-# if ($self->enable_picking) {
-# $self->mark_volumes_for_layer_height;
-# $self->volumes->set_print_box($self->bed_bounding_box->x_min, $self->bed_bounding_box->y_min, 0.0, $self->bed_bounding_box->x_max, $self->bed_bounding_box->y_max, $self->{config}->get('max_print_height'));
-# $self->volumes->check_outside_state($self->{config});
-# # do not cull backfaces to show broken geometry, if any
-# glDisable(GL_CULL_FACE);
-# }
-# $self->{plain_shader}->enable if $self->{plain_shader};
-# $self->volumes->render_VBOs;
-# $self->{plain_shader}->disable;
-# glEnable(GL_CULL_FACE) if ($self->enable_picking);
-# } else {
-# # do not cull backfaces to show broken geometry, if any
-# glDisable(GL_CULL_FACE) if ($self->enable_picking);
-# $self->volumes->render_legacy;
-# glEnable(GL_CULL_FACE) if ($self->enable_picking);
-# }
-#
-# if (defined $self->cutting_plane_z) {
-# # draw cutting plane
-# my $plane_z = $self->cutting_plane_z;
-# my $bb = $volumes_bb;
-# glDisable(GL_CULL_FACE);
-# glDisable(GL_LIGHTING);
-# glEnable(GL_BLEND);
-# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-# glBegin(GL_QUADS);
-# glColor4f(0.8, 0.8, 0.8, 0.5);
-# glVertex3f($bb->x_min-20, $bb->y_min-20, $plane_z);
-# glVertex3f($bb->x_max+20, $bb->y_min-20, $plane_z);
-# glVertex3f($bb->x_max+20, $bb->y_max+20, $plane_z);
-# glVertex3f($bb->x_min-20, $bb->y_max+20, $plane_z);
-# glEnd();
-# glEnable(GL_CULL_FACE);
-# glDisable(GL_BLEND);
-#
-# # draw cutting contours
-# glEnableClientState(GL_VERTEX_ARRAY);
-# glLineWidth(2);
-# glColor3f(0, 0, 0);
-# glVertexPointer_c(3, GL_FLOAT, 0, $self->cut_lines_vertices->ptr());
-# glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3);
-# glVertexPointer_c(3, GL_FLOAT, 0, 0);
-# glDisableClientState(GL_VERTEX_ARRAY);
-# }
-#
-# # draw warning message
-# $self->draw_warning;
-#
-# # draw gcode preview legend
-# $self->draw_legend;
-#
-# $self->draw_active_object_annotations;
-#
-# $self->SwapBuffers();
-#}
-#
-#sub draw_volumes {
-# # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking.
-# my ($self, $fakecolor) = @_;
-#
-# # do not cull backfaces to show broken geometry, if any
-# glDisable(GL_CULL_FACE);
-#
-# glEnable(GL_BLEND);
-# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-#
-# glEnableClientState(GL_VERTEX_ARRAY);
-# glEnableClientState(GL_NORMAL_ARRAY);
-#
-# foreach my $volume_idx (0..$#{$self->volumes}) {
-# my $volume = $self->volumes->[$volume_idx];
-#
-# if ($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;
-# my $b = ($volume_idx & 0x00FF0000) >> 16;
-# glColor4f($r/255.0, $g/255.0, $b/255.0, 1);
-# } elsif ($volume->selected) {
-# glColor4f(@{ &SELECTED_COLOR });
-# } elsif ($volume->hover) {
-# glColor4f(@{ &HOVER_COLOR });
-# } else {
-# glColor4f(@{ $volume->color });
-# }
-#
-# $volume->render;
-# }
-# glDisableClientState(GL_NORMAL_ARRAY);
-# glDisableClientState(GL_VERTEX_ARRAY);
-#
-# glDisable(GL_BLEND);
-# glEnable(GL_CULL_FACE);
-#}
-#
-#sub mark_volumes_for_layer_height {
-# my ($self) = @_;
-#
-# foreach my $volume_idx (0..$#{$self->volumes}) {
-# my $volume = $self->volumes->[$volume_idx];
-# my $object_id = int($volume->select_group_id / 1000000);
-# if ($self->layer_editing_enabled && $volume->selected && $self->{layer_height_edit_shader} &&
-# $volume->has_layer_height_texture && $object_id < $self->{print}->object_count) {
-# $volume->set_layer_height_texture_data($self->{layer_preview_z_texture_id}, $self->{layer_height_edit_shader}->shader_program_id,
-# $self->{print}->get_object($object_id), $self->_variable_layer_thickness_bar_mouse_cursor_z_relative, $self->{layer_height_edit_band_width});
-# } else {
-# $volume->reset_layer_height_texture_data();
-# }
-# }
-#}
-#
-#sub _load_image_set_texture {
-# my ($self, $file_name) = @_;
-# # Load a PNG with an alpha channel.
-# my $img = Wx::Image->new;
-# $img->LoadFile(Slic3r::var($file_name), wxBITMAP_TYPE_PNG);
-# # Get RGB & alpha raw data from wxImage, interleave them into a Perl array.
-# my @rgb = unpack 'C*', $img->GetData();
-# my @alpha = $img->HasAlpha ? unpack 'C*', $img->GetAlpha() : (255) x (int(@rgb) / 3);
-# my $n_pixels = int(@alpha);
-# my @data = (0)x($n_pixels * 4);
-# for (my $i = 0; $i < $n_pixels; $i += 1) {
-# $data[$i*4 ] = $rgb[$i*3];
-# $data[$i*4+1] = $rgb[$i*3+1];
-# $data[$i*4+2] = $rgb[$i*3+2];
-# $data[$i*4+3] = $alpha[$i];
-# }
-# # Initialize a raw bitmap data.
-# my $params = {
-# loaded => 1,
-# valid => $n_pixels > 0,
-# width => $img->GetWidth,
-# height => $img->GetHeight,
-# data => OpenGL::Array->new_list(GL_UNSIGNED_BYTE, @data),
-# texture_id => glGenTextures_p(1)
-# };
-# # Create and initialize a texture with the raw data.
-# glBindTexture(GL_TEXTURE_2D, $params->{texture_id});
-# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
-# glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $params->{width}, $params->{height}, 0, GL_RGBA, GL_UNSIGNED_BYTE, $params->{data}->ptr);
-# glBindTexture(GL_TEXTURE_2D, 0);
-# return $params;
-#}
-#
-#sub _variable_layer_thickness_load_overlay_image {
-# my ($self) = @_;
-# $self->{layer_preview_annotation} = $self->_load_image_set_texture('variable_layer_height_tooltip.png')
-# if (! $self->{layer_preview_annotation}->{loaded});
-# return $self->{layer_preview_annotation}->{valid};
-#}
-#
-#sub _variable_layer_thickness_load_reset_image {
-# my ($self) = @_;
-# $self->{layer_preview_reset_image} = $self->_load_image_set_texture('variable_layer_height_reset.png')
-# if (! $self->{layer_preview_reset_image}->{loaded});
-# return $self->{layer_preview_reset_image}->{valid};
-#}
-#
-## Paint the tooltip.
-#sub _render_image {
-# my ($self, $image, $l, $r, $b, $t) = @_;
-# $self->_render_texture($image->{texture_id}, $l, $r, $b, $t);
-#}
-#
-#sub _render_texture {
-# my ($self, $tex_id, $l, $r, $b, $t) = @_;
-#
-# glColor4f(1.,1.,1.,1.);
-# glDisable(GL_LIGHTING);
-# glEnable(GL_BLEND);
-# glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-# glEnable(GL_TEXTURE_2D);
-# glBindTexture(GL_TEXTURE_2D, $tex_id);
-# glBegin(GL_QUADS);
-# glTexCoord2d(0.,1.); glVertex3f($l, $b, 0);
-# glTexCoord2d(1.,1.); glVertex3f($r, $b, 0);
-# glTexCoord2d(1.,0.); glVertex3f($r, $t, 0);
-# glTexCoord2d(0.,0.); glVertex3f($l, $t, 0);
-# glEnd();
-# glBindTexture(GL_TEXTURE_2D, 0);
-# glDisable(GL_TEXTURE_2D);
-# glDisable(GL_BLEND);
-# glEnable(GL_LIGHTING);
-#}
-#
-#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->{layer_height_edit_shader} || ! $self->layer_editing_enabled);
-#
-# # Find the selected volume, over which the layer editing is active.
-# my $volume;
-# foreach my $volume_idx (0..$#{$self->volumes}) {
-# my $v = $self->volumes->[$volume_idx];
-# if ($v->selected && $v->has_layer_height_texture) {
-# $volume = $v;
-# last;
-# }
-# }
-# return if (! $volume);
-#
-# # If the active object was not allocated at the Print, go away. This should only be a momentary case between an object addition / deletion
-# # and an update by Platter::async_apply_config.
-# my $object_idx = int($volume->select_group_id / 1000000);
-# return if $object_idx >= $self->{print}->object_count;
-#
-# # 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 ($bar_left, $bar_bottom, $bar_right, $bar_top) = $self->_variable_layer_thickness_bar_rect_viewport;
-# my ($reset_left, $reset_bottom, $reset_right, $reset_top) = $self->_variable_layer_thickness_reset_rect_viewport;
-# my $z_cursor_relative = $self->_variable_layer_thickness_bar_mouse_cursor_z_relative;
-#
-# my $print_object = $self->{print}->get_object($object_idx);
-# my $z_max = $print_object->model_object->bounding_box->z_max;
-#
-# $self->{layer_height_edit_shader}->enable;
-# $self->{layer_height_edit_shader}->set_uniform('z_to_texture_row', $volume->layer_height_texture_z_to_row_id);
-# $self->{layer_height_edit_shader}->set_uniform('z_texture_row_to_normalized', 1. / $volume->layer_height_texture_height);
-# $self->{layer_height_edit_shader}->set_uniform('z_cursor', $z_max * $z_cursor_relative);
-# $self->{layer_height_edit_shader}->set_uniform('z_cursor_band_width', $self->{layer_height_edit_band_width});
-# glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
-# glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $volume->layer_height_texture_width, $volume->layer_height_texture_height,
-# 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
-# glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2,
-# 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
-# glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $volume->layer_height_texture_width, $volume->layer_height_texture_height,
-# GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level0);
-# glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $volume->layer_height_texture_width / 2, $volume->layer_height_texture_height / 2,
-# GL_RGBA, GL_UNSIGNED_BYTE, $volume->layer_height_texture_data_ptr_level1);
-#
-# # 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, $z_max);
-# glVertex3f($bar_left, $bar_top, $z_max);
-# glEnd();
-# glBindTexture(GL_TEXTURE_2D, 0);
-# $self->{layer_height_edit_shader}->disable;
-#
-# # Paint the tooltip.
-# if ($self->_variable_layer_thickness_load_overlay_image)
-# my $gap = 10/$self->_zoom;
-# my ($l, $r, $b, $t) = ($bar_left - $self->{layer_preview_annotation}->{width}/$self->_zoom - $gap, $bar_left - $gap, $reset_bottom + $self->{layer_preview_annotation}->{height}/$self->_zoom + $gap, $reset_bottom + $gap);
-# $self->_render_image($self->{layer_preview_annotation}, $l, $r, $t, $b);
-# }
-#
-# # Paint the reset button.
-# if ($self->_variable_layer_thickness_load_reset_image) {
-# $self->_render_image($self->{layer_preview_reset_image}, $reset_left, $reset_right, $reset_bottom, $reset_top);
-# }
-#
-# # Paint the graph.
-# #FIXME show some kind of legend.
-# my $max_z = unscale($print_object->size->z);
-# my $profile = $print_object->model_object->layer_height_profile;
-# my $layer_height = $print_object->config->get('layer_height');
-# my $layer_height_max = 10000000000.;
-# {
-# # Get a maximum layer height value.
-# #FIXME This is a duplicate code of Slicing.cpp.
-# my $nozzle_diameters = $print_object->print->config->get('nozzle_diameter');
-# my $layer_heights_min = $print_object->print->config->get('min_layer_height');
-# my $layer_heights_max = $print_object->print->config->get('max_layer_height');
-# for (my $i = 0; $i < scalar(@{$nozzle_diameters}); $i += 1) {
-# my $lh_min = ($layer_heights_min->[$i] == 0.) ? 0.07 : max(0.01, $layer_heights_min->[$i]);
-# my $lh_max = ($layer_heights_max->[$i] == 0.) ? (0.75 * $nozzle_diameters->[$i]) : $layer_heights_max->[$i];
-# $layer_height_max = min($layer_height_max, max($lh_min, $lh_max));
-# }
-# }
-# # Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region.
-# $layer_height_max *= 1.12;
-# # Baseline
-# glColor3f(0., 0., 0.);
-# glBegin(GL_LINE_STRIP);
-# glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / $layer_height_max, $bar_bottom);
-# glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / $layer_height_max, $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) / $layer_height_max, $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z);
-# }
-# glEnd();
-# # Revert the matrices.
-# glPopMatrix();
-# glEnable(GL_DEPTH_TEST);
-#}
-#
-#sub draw_legend {
-# my ($self) = @_;
-#
-# if (!$self->_legend_enabled) {
-# return;
-# }
-#
-# # If the legend texture has not been loaded into the GPU, do it now.
-# my $tex_id = Slic3r::GUI::_3DScene::finalize_legend_texture;
-# if ($tex_id > 0)
-# {
-# my $tex_w = Slic3r::GUI::_3DScene::get_legend_texture_width;
-# my $tex_h = Slic3r::GUI::_3DScene::get_legend_texture_height;
-# if (($tex_w > 0) && ($tex_h > 0))
-# {
-# glDisable(GL_DEPTH_TEST);
-# glPushMatrix();
-# glLoadIdentity();
-#
-# my ($cw, $ch) = $self->GetSizeWH;
-#
-# my $l = (-0.5 * $cw) / $self->_zoom;
-# my $t = (0.5 * $ch) / $self->_zoom;
-# my $r = $l + $tex_w / $self->_zoom;
-# my $b = $t - $tex_h / $self->_zoom;
-# $self->_render_texture($tex_id, $l, $r, $b, $t);
-#
-# glPopMatrix();
-# glEnable(GL_DEPTH_TEST);
-# }
-# }
-#}
-#
-#sub draw_warning {
-# my ($self) = @_;
-#
-# if (!$self->_warning_enabled) {
-# return;
-# }
-#
-# # If the warning texture has not been loaded into the GPU, do it now.
-# my $tex_id = Slic3r::GUI::_3DScene::finalize_warning_texture;
-# if ($tex_id > 0)
-# {
-# my $tex_w = Slic3r::GUI::_3DScene::get_warning_texture_width;
-# my $tex_h = Slic3r::GUI::_3DScene::get_warning_texture_height;
-# if (($tex_w > 0) && ($tex_h > 0))
-# {
-# glDisable(GL_DEPTH_TEST);
-# glPushMatrix();
-# glLoadIdentity();
-#
-# my ($cw, $ch) = $self->GetSizeWH;
-#
-# my $l = (-0.5 * $tex_w) / $self->_zoom;
-# my $t = (-0.5 * $ch + $tex_h) / $self->_zoom;
-# my $r = $l + $tex_w / $self->_zoom;
-# my $b = $t - $tex_h / $self->_zoom;
-# $self->_render_texture($tex_id, $l, $r, $b, $t);
-#
-# glPopMatrix();
-# glEnable(GL_DEPTH_TEST);
-# }
-# }
-#}
-#
-#sub update_volumes_colors_by_extruder {
-# my ($self, $config) = @_;
-# $self->volumes->update_colors_by_extruder($config);
-#}
-#
-#sub opengl_info
-#{
-# my ($self, %params) = @_;
-# my %tag = Slic3r::tags($params{format});
-#
-# my $gl_version = glGetString(GL_VERSION);
-# my $gl_vendor = glGetString(GL_VENDOR);
-# my $gl_renderer = glGetString(GL_RENDERER);
-# my $glsl_version = glGetString(GL_SHADING_LANGUAGE_VERSION);
-#
-# my $out = '';
-# $out .= "$tag{h2start}OpenGL installation$tag{h2end}$tag{eol}";
-# $out .= " $tag{bstart}Using POGL$tag{bend} v$OpenGL::BUILD_VERSION$tag{eol}";
-# $out .= " $tag{bstart}GL version: $tag{bend}${gl_version}$tag{eol}";
-# $out .= " $tag{bstart}vendor: $tag{bend}${gl_vendor}$tag{eol}";
-# $out .= " $tag{bstart}renderer: $tag{bend}${gl_renderer}$tag{eol}";
-# $out .= " $tag{bstart}GLSL version: $tag{bend}${glsl_version}$tag{eol}";
-#
-# # Check for other OpenGL extensions
-# $out .= "$tag{h2start}Installed extensions (* implemented in the module):$tag{h2end}$tag{eol}";
-# my $extensions = glGetString(GL_EXTENSIONS);
-# my @extensions = split(' ',$extensions);
-# foreach my $ext (sort @extensions) {
-# my $stat = glpCheckExtension($ext);
-# $out .= sprintf("%s ${ext}$tag{eol}", $stat?' ':'*');
-# $out .= sprintf(" ${stat}$tag{eol}") if ($stat && $stat !~ m|^$ext |);
-# }
-#
-# return $out;
-#}
-#
-#sub _report_opengl_state
-#{
-# my ($self, $comment) = @_;
-# my $err = glGetError();
-# return 0 if ($err == 0);
-#
-# # gluErrorString() hangs. Don't use it.
-## my $errorstr = gluErrorString();
-# my $errorstr = '';
-# if ($err == 0x0500) {
-# $errorstr = 'GL_INVALID_ENUM';
-# } elsif ($err == GL_INVALID_VALUE) {
-# $errorstr = 'GL_INVALID_VALUE';
-# } elsif ($err == GL_INVALID_OPERATION) {
-# $errorstr = 'GL_INVALID_OPERATION';
-# } elsif ($err == GL_STACK_OVERFLOW) {
-# $errorstr = 'GL_STACK_OVERFLOW';
-# } elsif ($err == GL_OUT_OF_MEMORY) {
-# $errorstr = 'GL_OUT_OF_MEMORY';
-# } else {
-# $errorstr = 'unknown';
-# }
-# if (defined($comment)) {
-# printf("OpenGL error at %s, nr %d (0x%x): %s\n", $comment, $err, $err, $errorstr);
-# } else {
-# printf("OpenGL error nr %d (0x%x): %s\n", $err, $err, $errorstr);
-# }
-#}
-#
-#sub _vertex_shader_Gouraud {
-# return <<'VERTEX';
-##version 110
-#
-##define INTENSITY_CORRECTION 0.6
-#
-#// normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
-#const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
-##define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
-##define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
-##define LIGHT_TOP_SHININESS 20.0
-#
-#// normalized values for (1./1.43, 0.2/1.43, 1./1.43)
-#const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
-##define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
-#//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
-#//#define LIGHT_FRONT_SHININESS 5.0
-#
-##define INTENSITY_AMBIENT 0.3
-#
-#const vec3 ZERO = vec3(0.0, 0.0, 0.0);
-#
-#struct PrintBoxDetection
-#{
-# vec3 min;
-# vec3 max;
-# // xyz contains the offset, if w == 1.0 detection needs to be performed
-# vec4 volume_origin;
-#};
-#
-#uniform PrintBoxDetection print_box;
-#
-#// x = tainted, y = specular;
-#varying vec2 intensity;
-#
-#varying vec3 delta_box_min;
-#varying vec3 delta_box_max;
-#
-#void main()
-#{
-# // First transform the normal into camera space and normalize the result.
-# vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
-#
-# // 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.
-# float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
-#
-# intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
-# intensity.y = 0.0;
-#
-# if (NdotL > 0.0)
-# intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS);
-#
-# // Perform the same lighting calculation for the 2nd light source (no specular applied).
-# NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
-# intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
-#
-# // compute deltas for out of print volume detection (world coordinates)
-# if (print_box.volume_origin.w == 1.0)
-# {
-# vec3 v = gl_Vertex.xyz + print_box.volume_origin.xyz;
-# delta_box_min = v - print_box.min;
-# delta_box_max = v - print_box.max;
-# }
-# else
-# {
-# delta_box_min = ZERO;
-# delta_box_max = ZERO;
-# }
-#
-# gl_Position = ftransform();
-#}
-#
-#VERTEX
-#}
-#
-#sub _fragment_shader_Gouraud {
-# return <<'FRAGMENT';
-##version 110
-#
-#const vec3 ZERO = vec3(0.0, 0.0, 0.0);
-#
-#// x = tainted, y = specular;
-#varying vec2 intensity;
-#
-#varying vec3 delta_box_min;
-#varying vec3 delta_box_max;
-#
-#uniform vec4 uniform_color;
-#
-#void main()
-#{
-# // if the fragment is outside the print volume -> use darker color
-# vec3 color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? mix(uniform_color.rgb, ZERO, 0.3333) : uniform_color.rgb;
-# gl_FragColor = vec4(vec3(intensity.y, intensity.y, intensity.y) + color * intensity.x, uniform_color.a);
-#}
-#
-#FRAGMENT
-#}
-#
-#sub _vertex_shader_Phong {
-# return <<'VERTEX';
-##version 110
-#
-#varying vec3 normal;
-#varying vec3 eye;
-#void main(void)
-#{
-# eye = normalize(vec3(gl_ModelViewMatrix * gl_Vertex));
-# normal = normalize(gl_NormalMatrix * gl_Normal);
-# gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
-#}
-#VERTEX
-#}
-#
-#sub _fragment_shader_Phong {
-# return <<'FRAGMENT';
-##version 110
-#
-##define INTENSITY_CORRECTION 0.7
-#
-##define LIGHT_TOP_DIR -0.6/1.31, 0.6/1.31, 1./1.31
-##define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
-##define LIGHT_TOP_SPECULAR (0.5 * INTENSITY_CORRECTION)
-#//#define LIGHT_TOP_SHININESS 50.
-##define LIGHT_TOP_SHININESS 10.
-#
-##define LIGHT_FRONT_DIR 1./1.43, 0.2/1.43, 1./1.43
-##define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
-##define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
-##define LIGHT_FRONT_SHININESS 50.
-#
-##define INTENSITY_AMBIENT 0.0
-#
-#varying vec3 normal;
-#varying vec3 eye;
-#uniform vec4 uniform_color;
-#void main() {
-#
-# float intensity_specular = 0.;
-# float intensity_tainted = 0.;
-# float intensity = max(dot(normal,vec3(LIGHT_TOP_DIR)), 0.0);
-# // if the vertex is lit compute the specular color
-# if (intensity > 0.0) {
-# intensity_tainted = LIGHT_TOP_DIFFUSE * intensity;
-# // compute the half vector
-# vec3 h = normalize(vec3(LIGHT_TOP_DIR) + eye);
-# // compute the specular term into spec
-# intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(h, normal), 0.0), LIGHT_TOP_SHININESS);
-# }
-# intensity = max(dot(normal,vec3(LIGHT_FRONT_DIR)), 0.0);
-# // if the vertex is lit compute the specular color
-# if (intensity > 0.0) {
-# intensity_tainted += LIGHT_FRONT_DIFFUSE * intensity;
-# // compute the half vector
-#// vec3 h = normalize(vec3(LIGHT_FRONT_DIR) + eye);
-# // compute the specular term into spec
-#// intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(h,normal), 0.0), LIGHT_FRONT_SHININESS);
-# }
-#
-# gl_FragColor = max(
-# vec4(intensity_specular, intensity_specular, intensity_specular, 0.) + uniform_color * intensity_tainted,
-# INTENSITY_AMBIENT * uniform_color);
-# gl_FragColor.a = uniform_color.a;
-#}
-#FRAGMENT
-#}
-#
-#sub _vertex_shader_variable_layer_height {
-# return <<'VERTEX';
-##version 110
-#
-##define INTENSITY_CORRECTION 0.6
-#
-#const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929);
-##define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
-##define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION)
-##define LIGHT_TOP_SHININESS 20.0
-#
-#const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
-##define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
-#//#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
-#//#define LIGHT_FRONT_SHININESS 5.0
-#
-##define INTENSITY_AMBIENT 0.3
-#
-#// x = tainted, y = specular;
-#varying vec2 intensity;
-#
-#varying float object_z;
-#
-#void main()
-#{
-# // First transform the normal into camera space and normalize the result.
-# vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
-#
-# // 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.
-# float NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
-#
-# intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
-# intensity.y = 0.0;
-#
-# if (NdotL > 0.0)
-# intensity.y += LIGHT_TOP_SPECULAR * pow(max(dot(normal, reflect(-LIGHT_TOP_DIR, normal)), 0.0), LIGHT_TOP_SHININESS);
-#
-# // Perform the same lighting calculation for the 2nd light source (no specular)
-# NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
-#
-# intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
-#
-# // Scaled to widths of the Z texture.
-# object_z = gl_Vertex.z;
-#
-# gl_Position = ftransform();
-#}
-#
-#VERTEX
-#}
-#
-#sub _fragment_shader_variable_layer_height {
-# 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;
-#uniform float z_cursor;
-#uniform float z_cursor_band_width;
-#
-#// x = tainted, y = specular;
-#varying vec2 intensity;
-#
-#varying float object_z;
-#
-#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.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25;
-# // Calculate level of detail from the object Z coordinate.
-# // This makes the slowly sloping surfaces to be show with high detail (with stripes),
-# // and the vertical surfaces to be shown with low detail (no stripes)
-# float z_in_cells = object_z_row * 190.;
-# // Gradient of Z projected on the screen.
-# float dx_vtc = dFdx(z_in_cells);
-# float dy_vtc = dFdy(z_in_cells);
-# float lod = clamp(0.5 * log2(max(dx_vtc*dx_vtc, dy_vtc*dy_vtc)), 0., 1.);
-# // Sample the Z texture. Texture coordinates are normalized to <0, 1>.
-# vec4 color =
-# mix(texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5 )), -10000.),
-# texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row * 2. + 1.)), 10000.), lod);
-#
-# // Mix the final color.
-# gl_FragColor =
-# vec4(intensity.y, intensity.y, intensity.y, 1.0) + intensity.x * mix(color, vec4(1.0, 1.0, 0.0, 1.0), z_blend);
-#}
-#
-#FRAGMENT
-#}
-#===================================================================================================================================
-
# The 3D canvas to display objects and tool paths.
package Slic3r::GUI::3DScene;
use base qw(Slic3r::GUI::3DScene::Base);
-#===================================================================================================================================
-#use OpenGL qw(:glconstants :gluconstants :glufunctions);
-#use List::Util qw(first min max);
-#use Slic3r::Geometry qw(scale unscale epsilon);
-#use Slic3r::Print::State ':steps';
-#===================================================================================================================================
-
-#===================================================================================================================================
-#__PACKAGE__->mk_accessors(qw(
-# color_by
-# select_by
-# drag_by
-#));
-#===================================================================================================================================
-
sub new {
my $class = shift;
- my $self = $class->SUPER::new(@_);
-#===================================================================================================================================
-# $self->color_by('volume'); # object | volume
-# $self->select_by('object'); # object | volume | instance
-# $self->drag_by('instance'); # object | instance
-#===================================================================================================================================
-
+ my $self = $class->SUPER::new(@_);
return $self;
}
-#==============================================================================================================================
-#sub load_object {
-# my ($self, $model, $print, $obj_idx, $instance_idxs) = @_;
-#
-# $self->SetCurrent($self->GetContext) if $useVBOs;
-#
-# my $model_object;
-# if ($model->isa('Slic3r::Model::Object')) {
-# $model_object = $model;
-# $model = $model_object->model;
-# $obj_idx = 0;
-# } else {
-# $model_object = $model->get_object($obj_idx);
-# }
-#
-# $instance_idxs ||= [0..$#{$model_object->instances}];
-# my $volume_indices = $self->volumes->load_object(
-# $model_object, $obj_idx, $instance_idxs, $self->color_by, $self->select_by, $self->drag_by,
-# $self->UseVBOs);
-# return @{$volume_indices};
-#}
-#
-## Create 3D thick extrusion lines for a skirt and brim.
-## Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes.
-#sub load_print_toolpaths {
-# my ($self, $print, $colors) = @_;
-#
-# $self->SetCurrent($self->GetContext) if $self->UseVBOs;
-# Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes, $colors, $self->UseVBOs)
-# if ($print->step_done(STEP_SKIRT) && $print->step_done(STEP_BRIM));
-#}
-#
-## Create 3D thick extrusion lines for object forming extrusions.
-## Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
-## one for perimeters, one for infill and one for supports.
-#sub load_print_object_toolpaths {
-# my ($self, $object, $colors) = @_;
-#
-# $self->SetCurrent($self->GetContext) if $self->UseVBOs;
-# Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $colors, $self->UseVBOs);
-#}
-#
-## Create 3D thick extrusion lines for wipe tower extrusions.
-#sub load_wipe_tower_toolpaths {
-# my ($self, $print, $colors) = @_;
-#
-# $self->SetCurrent($self->GetContext) if $self->UseVBOs;
-# Slic3r::GUI::_3DScene::_load_wipe_tower_toolpaths($print, $self->volumes, $colors, $self->UseVBOs)
-# if ($print->step_done(STEP_WIPE_TOWER));
-#}
-#
-#sub load_gcode_preview {
-# my ($self, $print, $gcode_preview_data, $colors) = @_;
-#
-# $self->SetCurrent($self->GetContext) if $self->UseVBOs;
-# Slic3r::GUI::_3DScene::load_gcode_preview($print, $gcode_preview_data, $self->volumes, $colors, $self->UseVBOs);
-#}
-#
-#sub set_toolpaths_range {
-# my ($self, $min_z, $max_z) = @_;
-# $self->volumes->set_range($min_z, $max_z);
-#}
-#
-#sub reset_legend_texture {
-# Slic3r::GUI::_3DScene::reset_legend_texture();
-#}
-#
-#sub get_current_print_zs {
-# my ($self, $active_only) = @_;
-# return $self->volumes->get_current_print_zs($active_only);
-#}
-#==============================================================================================================================
-
1;
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 73f0e14cb..4d421dc4a 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -14,6 +14,7 @@ use Wx qw(:button :colour :cursor :dialog :filedialog :keycode :icon :font :id :
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_LEFT_DOWN EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED);
+use Slic3r::Geometry qw(PI);
use base 'Wx::Panel';
use constant TB_ADD => &Wx::NewId;
@@ -116,6 +117,8 @@ sub new {
my $model_object = $self->{model}->objects->[$obj_idx];
my $model_instance = $model_object->instances->[0];
+
+ $self->stop_background_process;
my $variation = $scale / $model_instance->scaling_factor;
#FIXME Scale the layer height profile?
@@ -127,7 +130,6 @@ sub new {
$object->transform_thumbnail($self->{model}, $obj_idx);
#update print and start background processing
- $self->stop_background_process;
$self->{print}->add_model_object($model_object, $obj_idx);
$self->selection_changed(1); # refresh info (size, volume etc.)
@@ -135,6 +137,27 @@ sub new {
$self->schedule_background_process;
};
+ # callback to react to gizmo rotate
+ my $on_gizmo_rotate = sub {
+ my ($angle_z) = @_;
+ $self->rotate(rad2deg($angle_z), Z, 'absolute');
+ };
+
+ # callback to update object's geometry info while using gizmos
+ my $on_update_geometry_info = sub {
+ my ($size_x, $size_y, $size_z, $scale_factor) = @_;
+
+ my ($obj_idx, $object) = $self->selected_object;
+
+ if ((defined $obj_idx) && ($self->{object_info_size})) { # have we already loaded the info pane?
+ $self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", $size_x, $size_y, $size_z));
+ my $model_object = $self->{model}->objects->[$obj_idx];
+ if (my $stats = $model_object->mesh_stats) {
+ $self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * $scale_factor**3));
+ }
+ }
+ };
+
# Initialize 3D plater
if ($Slic3r::GUI::have_OpenGL) {
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config});
@@ -152,7 +175,9 @@ sub new {
Slic3r::GUI::_3DScene::register_on_instance_moved_callback($self->{canvas3D}, $on_instances_moved);
Slic3r::GUI::_3DScene::register_on_enable_action_buttons_callback($self->{canvas3D}, $enable_action_buttons);
Slic3r::GUI::_3DScene::register_on_gizmo_scale_uniformly_callback($self->{canvas3D}, $on_gizmo_scale_uniformly);
-# Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1);
+ Slic3r::GUI::_3DScene::register_on_gizmo_rotate_callback($self->{canvas3D}, $on_gizmo_rotate);
+ Slic3r::GUI::_3DScene::register_on_update_geometry_info_callback($self->{canvas3D}, $on_update_geometry_info);
+ Slic3r::GUI::_3DScene::enable_gizmos($self->{canvas3D}, 1);
Slic3r::GUI::_3DScene::enable_shader($self->{canvas3D}, 1);
Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($self->{canvas3D}, 1);
@@ -1059,7 +1084,17 @@ sub rotate {
if ($axis == Z) {
my $new_angle = deg2rad($angle);
- $_->set_rotation(($relative ? $_->rotation : 0.) + $new_angle) for @{ $model_object->instances };
+ foreach my $inst (@{ $model_object->instances }) {
+ my $rotation = ($relative ? $inst->rotation : 0.) + $new_angle;
+ while ($rotation > 2.0 * PI) {
+ $rotation -= 2.0 * PI;
+ }
+ while ($rotation < 0.0) {
+ $rotation += 2.0 * PI;
+ }
+ $inst->set_rotation($rotation);
+ Slic3r::GUI::_3DScene::update_gizmos_data($self->{canvas3D}) if ($self->{canvas3D});
+ }
$object->transform_thumbnail($self->{model}, $obj_idx);
} else {
# rotation around X and Y needs to be performed on mesh
@@ -1667,34 +1702,6 @@ sub export_object_stl {
$self->statusbar->SetStatusText(L("STL file exported to ").$output_file);
}
-sub fix_through_netfabb {
- my ($self) = @_;
- my ($obj_idx, $object) = $self->selected_object;
- return if !defined $obj_idx;
- my $model_object = $self->{model}->objects->[$obj_idx];
- my $model_fixed = Slic3r::Model->new;
- Slic3r::GUI::fix_model_by_win10_sdk_gui($model_object, $self->{print}, $model_fixed);
-
- my @new_obj_idx = $self->load_model_objects(@{$model_fixed->objects});
- return if !@new_obj_idx;
-
- foreach my $new_obj_idx (@new_obj_idx) {
- my $o = $self->{model}->objects->[$new_obj_idx];
- $o->clear_instances;
- $o->add_instance($_) for @{$model_object->instances};
- #$o->invalidate_bounding_box;
-
- if ($o->volumes_count == $model_object->volumes_count) {
- for my $i (0..($o->volumes_count-1)) {
- $o->get_volume($i)->config->apply($model_object->get_volume($i)->config);
- }
- }
- #FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile, layer_height_profile_valid,
- }
-
- $self->remove($obj_idx);
-}
-
sub export_amf {
my ($self) = @_;
return if !@{$self->{objects}};
@@ -2273,11 +2280,6 @@ sub object_menu {
$frame->_append_menu_item($menu, L("Export object as STL…"), L('Export this single object as STL file'), sub {
$self->export_object_stl;
}, undef, 'brick_go.png');
- if (Slic3r::GUI::is_windows10) {
- $frame->_append_menu_item($menu, L("Fix STL through Netfabb"), L('Fix the model by sending it to a Netfabb cloud service through Windows 10 API'), sub {
- $self->fix_through_netfabb;
- }, undef, 'brick_go.png');
- }
return $menu;
}
diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm
index 7f83e0f57..0b770b31c 100644
--- a/lib/Slic3r/GUI/Plater/3D.pm
+++ b/lib/Slic3r/GUI/Plater/3D.pm
@@ -5,25 +5,13 @@ use utf8;
use List::Util qw();
use Wx qw(:misc :pen :brush :sizer :font :cursor :keycode wxTAB_TRAVERSAL);
-#==============================================================================================================================
-#use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR);
-#==============================================================================================================================
use base qw(Slic3r::GUI::3DScene Class::Accessor);
-#==============================================================================================================================
-#use Wx::Locale gettext => 'L';
-#
-#__PACKAGE__->mk_accessors(qw(
-# on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly
-# on_remove_object on_increase_objects on_decrease_objects on_enable_action_buttons));
-#==============================================================================================================================
-
sub new {
my $class = shift;
my ($parent, $objects, $model, $print, $config) = @_;
my $self = $class->SUPER::new($parent);
-#==============================================================================================================================
Slic3r::GUI::_3DScene::enable_picking($self, 1);
Slic3r::GUI::_3DScene::enable_moving($self, 1);
Slic3r::GUI::_3DScene::set_select_by($self, 'object');
@@ -31,253 +19,8 @@ sub new {
Slic3r::GUI::_3DScene::set_model($self, $model);
Slic3r::GUI::_3DScene::set_print($self, $print);
Slic3r::GUI::_3DScene::set_config($self, $config);
-# $self->enable_picking(1);
-# $self->enable_moving(1);
-# $self->select_by('object');
-# $self->drag_by('instance');
-#
-# $self->{objects} = $objects;
-# $self->{model} = $model;
-# $self->{print} = $print;
-# $self->{config} = $config;
-# $self->{on_select_object} = sub {};
-# $self->{on_instances_moved} = sub {};
-# $self->{on_wipe_tower_moved} = sub {};
-#
-# $self->{objects_volumes_idxs} = [];
-#
-# $self->on_select(sub {
-# my ($volume_idx) = @_;
-# $self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx)
-# if ($self->{on_select_object});
-# });
-#
-# $self->on_move(sub {
-# my @volume_idxs = @_;
-# my %done = (); # prevent moving instances twice
-# my $object_moved;
-# my $wipe_tower_moved;
-# foreach my $volume_idx (@volume_idxs) {
-# my $volume = $self->volumes->[$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;
-# if ($obj_idx < 1000) {
-# # Move a regular object.
-# my $model_object = $self->{model}->get_object($obj_idx);
-# $model_object
-# ->instances->[$instance_idx]
-# ->offset
-# ->translate($volume->origin->x, $volume->origin->y); #))
-# $model_object->invalidate_bounding_box;
-# $object_moved = 1;
-# } elsif ($obj_idx == 1000) {
-# # Move a wipe tower proxy.
-# $wipe_tower_moved = $volume->origin;
-# }
-# }
-#
-# $self->{on_instances_moved}->()
-# if $object_moved && $self->{on_instances_moved};
-# $self->{on_wipe_tower_moved}->($wipe_tower_moved)
-# if $wipe_tower_moved && $self->{on_wipe_tower_moved};
-# });
-#
-# EVT_KEY_DOWN($self, sub {
-# my ($s, $event) = @_;
-# if ($event->HasModifiers) {
-# $event->Skip;
-# } else {
-# my $key = $event->GetKeyCode;
-# if ($key == WXK_DELETE) {
-# $self->on_remove_object->() if $self->on_remove_object;
-# } else {
-# $event->Skip;
-# }
-# }
-# });
-#
-# EVT_CHAR($self, sub {
-# my ($s, $event) = @_;
-# if ($event->HasModifiers) {
-# $event->Skip;
-# } else {
-# my $key = $event->GetKeyCode;
-# if ($key == ord('a')) {
-# $self->on_arrange->() if $self->on_arrange;
-# } elsif ($key == ord('l')) {
-# $self->on_rotate_object_left->() if $self->on_rotate_object_left;
-# } elsif ($key == ord('r')) {
-# $self->on_rotate_object_right->() if $self->on_rotate_object_right;
-# } elsif ($key == ord('s')) {
-# $self->on_scale_object_uniformly->() if $self->on_scale_object_uniformly;
-# } elsif ($key == ord('+')) {
-# $self->on_increase_objects->() if $self->on_increase_objects;
-# } elsif ($key == ord('-')) {
-# $self->on_decrease_objects->() if $self->on_decrease_objects;
-# } else {
-# $event->Skip;
-# }
-# }
-# });
-#==============================================================================================================================
return $self;
}
-#==============================================================================================================================
-#sub set_on_select_object {
-# my ($self, $cb) = @_;
-# $self->{on_select_object} = $cb;
-#}
-#
-#sub set_on_double_click {
-# my ($self, $cb) = @_;
-# $self->on_double_click($cb);
-#}
-#
-#sub set_on_right_click {
-# my ($self, $cb) = @_;
-# $self->on_right_click($cb);
-#}
-#
-#sub set_on_arrange {
-# my ($self, $cb) = @_;
-# $self->on_arrange($cb);
-#}
-#
-#sub set_on_rotate_object_left {
-# my ($self, $cb) = @_;
-# $self->on_rotate_object_left($cb);
-#}
-#
-#sub set_on_rotate_object_right {
-# my ($self, $cb) = @_;
-# $self->on_rotate_object_right($cb);
-#}
-#
-#sub set_on_scale_object_uniformly {
-# my ($self, $cb) = @_;
-# $self->on_scale_object_uniformly($cb);
-#}
-#
-#sub set_on_increase_objects {
-# my ($self, $cb) = @_;
-# $self->on_increase_objects($cb);
-#}
-#
-#sub set_on_decrease_objects {
-# my ($self, $cb) = @_;
-# $self->on_decrease_objects($cb);
-#}
-#
-#sub set_on_remove_object {
-# my ($self, $cb) = @_;
-# $self->on_remove_object($cb);
-#}
-#
-#sub set_on_instances_moved {
-# my ($self, $cb) = @_;
-# $self->{on_instances_moved} = $cb;
-#}
-#
-#sub set_on_wipe_tower_moved {
-# my ($self, $cb) = @_;
-# $self->{on_wipe_tower_moved} = $cb;
-#}
-#
-#sub set_on_model_update {
-# my ($self, $cb) = @_;
-# $self->on_model_update($cb);
-#}
-#
-#sub set_on_enable_action_buttons {
-# my ($self, $cb) = @_;
-# $self->on_enable_action_buttons($cb);
-#}
-#
-#sub update_volumes_selection {
-# my ($self) = @_;
-#
-# foreach my $obj_idx (0..$#{$self->{model}->objects}) {
-# if ($self->{objects}[$obj_idx]->selected) {
-# my $volume_idxs = $self->{objects_volumes_idxs}->[$obj_idx];
-# $self->select_volume($_) for @{$volume_idxs};
-# }
-# }
-#}
-#
-#sub reload_scene {
-# my ($self, $force) = @_;
-#
-# $self->reset_objects;
-# $self->update_bed_size;
-#
-# if (! $self->IsShown && ! $force) {
-# $self->{reload_delayed} = 1;
-# return;
-# }
-#
-# $self->{reload_delayed} = 0;
-#
-# $self->{objects_volumes_idxs} = [];
-# foreach my $obj_idx (0..$#{$self->{model}->objects}) {
-# my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx);
-# push(@{$self->{objects_volumes_idxs}}, \@volume_idxs);
-# }
-#
-# $self->update_volumes_selection;
-#
-# if (defined $self->{config}->nozzle_diameter) {
-# # Should the wipe tower be visualized?
-# my $extruders_count = scalar @{ $self->{config}->nozzle_diameter };
-# # Height of a print.
-# my $height = $self->{model}->bounding_box->z_max;
-# # Show at least a slab.
-# $height = 10 if $height < 10;
-# if ($extruders_count > 1 && $self->{config}->single_extruder_multi_material && $self->{config}->wipe_tower &&
-# ! $self->{config}->complete_objects) {
-# $self->volumes->load_wipe_tower_preview(1000,
-# $self->{config}->wipe_tower_x, $self->{config}->wipe_tower_y, $self->{config}->wipe_tower_width,
-# #$self->{config}->wipe_tower_per_color_wipe# 15 * ($extruders_count - 1), # this is just a hack when the config parameter became obsolete
-# 15 * ($extruders_count - 1),
-# $self->{model}->bounding_box->z_max, $self->{config}->wipe_tower_rotation_angle, $self->UseVBOs);
-# }
-# }
-#
-# $self->update_volumes_colors_by_extruder($self->{config});
-#
-# # checks for geometry outside the print volume to render it accordingly
-# if (scalar @{$self->volumes} > 0)
-# {
-# my $contained = $self->volumes->check_outside_state($self->{config});
-# if (!$contained) {
-# $self->set_warning_enabled(1);
-# Slic3r::GUI::_3DScene::generate_warning_texture(L("Detected object outside print volume"));
-# $self->on_enable_action_buttons->(0) if ($self->on_enable_action_buttons);
-# } else {
-# $self->set_warning_enabled(0);
-# $self->volumes->reset_outside_state();
-# Slic3r::GUI::_3DScene::reset_warning_texture();
-# $self->on_enable_action_buttons->(scalar @{$self->{model}->objects} > 0) if ($self->on_enable_action_buttons);
-# }
-# } else {
-# $self->set_warning_enabled(0);
-# Slic3r::GUI::_3DScene::reset_warning_texture();
-# }
-#}
-#
-#sub update_bed_size {
-# my ($self) = @_;
-# $self->set_bed_shape($self->{config}->bed_shape);
-#}
-#
-## Called by the Platter wxNotebook when this page is activated.
-#sub OnActivate {
-# my ($self) = @_;
-# $self->reload_scene(1) if ($self->{reload_delayed});
-#}
-#==============================================================================================================================
-
1;
diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 1ec0ce1cb..a20a241f7 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -322,7 +322,13 @@ sub selection_changed {
}
# get default values
my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
-
+
+ # decide which settings will be shown by default
+ if ($itemData->{type} eq 'object') {
+ $config->set_ifndef('wipe_into_objects', 0);
+ $config->set_ifndef('wipe_into_infill', 0);
+ }
+
# append default extruder
push @opt_keys, 'extruder';
$default_config->set('extruder', 0);
@@ -330,7 +336,14 @@ sub selection_changed {
$self->{settings_panel}->set_default_config($default_config);
$self->{settings_panel}->set_config($config);
$self->{settings_panel}->set_opt_keys(\@opt_keys);
- $self->{settings_panel}->set_fixed_options([qw(extruder)]);
+
+ # disable minus icon to remove the settings
+ if ($itemData->{type} eq 'object') {
+ $self->{settings_panel}->set_fixed_options([qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)]);
+ } else {
+ $self->{settings_panel}->set_fixed_options([qw(extruder)]);
+ }
+
$self->{settings_panel}->enable;
}
diff --git a/resources/shaders/gouraud.vs b/resources/shaders/gouraud.vs
index 22ba91a93..ea7e46e79 100644
--- a/resources/shaders/gouraud.vs
+++ b/resources/shaders/gouraud.vs
@@ -22,8 +22,8 @@ struct PrintBoxDetection
{
vec3 min;
vec3 max;
- // xyz contains the offset, if w == 1.0 detection needs to be performed
- vec4 volume_origin;
+ bool volume_detection;
+ mat4 volume_world_matrix;
};
uniform PrintBoxDetection print_box;
@@ -54,9 +54,9 @@ void main()
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
// compute deltas for out of print volume detection (world coordinates)
- if (print_box.volume_origin.w == 1.0)
+ if (print_box.volume_detection)
{
- vec3 v = gl_Vertex.xyz + print_box.volume_origin.xyz;
+ vec3 v = (print_box.volume_world_matrix * gl_Vertex).xyz;
delta_box_min = v - print_box.min;
delta_box_max = v - print_box.max;
}
diff --git a/resources/shaders/variable_layer_height.vs b/resources/shaders/variable_layer_height.vs
index 2c918c0d4..9763859d0 100644
--- a/resources/shaders/variable_layer_height.vs
+++ b/resources/shaders/variable_layer_height.vs
@@ -14,6 +14,8 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define INTENSITY_AMBIENT 0.3
+uniform mat4 volume_world_matrix;
+
// x = tainted, y = specular;
varying vec2 intensity;
@@ -38,9 +40,8 @@ void main()
NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
intensity.x += NdotL * LIGHT_FRONT_DIFFUSE;
-
- // Scaled to widths of the Z texture.
- object_z = gl_Vertex.z;
+ // Scaled to widths of the Z texture.
+ object_z = (volume_world_matrix * gl_Vertex).z;
gl_Position = ftransform();
}
diff --git a/t/combineinfill.t b/t/combineinfill.t
index 5402a84f5..563ecb9c1 100644
--- a/t/combineinfill.t
+++ b/t/combineinfill.t
@@ -61,6 +61,7 @@ plan tests => 8;
$config->set('infill_every_layers', 2);
$config->set('perimeter_extruder', 1);
$config->set('infill_extruder', 2);
+ $config->set('wipe_into_infill', 0);
$config->set('support_material_extruder', 3);
$config->set('support_material_interface_extruder', 3);
$config->set('top_solid_layers', 0);
diff --git a/t/fill.t b/t/fill.t
index dd9eee487..5cbd568dd 100644
--- a/t/fill.t
+++ b/t/fill.t
@@ -201,6 +201,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
$config->set('bottom_solid_layers', 0);
$config->set('infill_extruder', 2);
$config->set('infill_extrusion_width', 0.5);
+ $config->set('wipe_into_infill', 0);
$config->set('fill_density', 40);
$config->set('cooling', [ 0 ]); # for preventing speeds from being altered
$config->set('first_layer_speed', '100%'); # for preventing speeds from being altered
diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp
index ceb968a50..4355cd61b 100644
--- a/xs/src/libslic3r/BoundingBox.cpp
+++ b/xs/src/libslic3r/BoundingBox.cpp
@@ -2,6 +2,8 @@
#include <algorithm>
#include <assert.h>
+#include <Eigen/Dense>
+
namespace Slic3r {
template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points);
@@ -251,4 +253,41 @@ void BoundingBox::align_to_grid(const coord_t cell_size)
}
}
+BoundingBoxf3 BoundingBoxf3::transformed(const std::vector<float>& matrix) const
+{
+ Eigen::Matrix<float, 3, 8> vertices;
+
+ vertices(0, 0) = (float)min.x; vertices(1, 0) = (float)min.y; vertices(2, 0) = (float)min.z;
+ vertices(0, 1) = (float)max.x; vertices(1, 1) = (float)min.y; vertices(2, 1) = (float)min.z;
+ vertices(0, 2) = (float)max.x; vertices(1, 2) = (float)max.y; vertices(2, 2) = (float)min.z;
+ vertices(0, 3) = (float)min.x; vertices(1, 3) = (float)max.y; vertices(2, 3) = (float)min.z;
+ vertices(0, 4) = (float)min.x; vertices(1, 4) = (float)min.y; vertices(2, 4) = (float)max.z;
+ vertices(0, 5) = (float)max.x; vertices(1, 5) = (float)min.y; vertices(2, 5) = (float)max.z;
+ vertices(0, 6) = (float)max.x; vertices(1, 6) = (float)max.y; vertices(2, 6) = (float)max.z;
+ vertices(0, 7) = (float)min.x; vertices(1, 7) = (float)max.y; vertices(2, 7) = (float)max.z;
+
+ Eigen::Transform<float, 3, Eigen::Affine> m;
+ ::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float));
+ Eigen::Matrix<float, 3, 8> transf_vertices = m * vertices.colwise().homogeneous();
+
+ float min_x = transf_vertices(0, 0);
+ float max_x = transf_vertices(0, 0);
+ float min_y = transf_vertices(1, 0);
+ float max_y = transf_vertices(1, 0);
+ float min_z = transf_vertices(2, 0);
+ float max_z = transf_vertices(2, 0);
+
+ for (int i = 1; i < 8; ++i)
+ {
+ min_x = std::min(min_x, transf_vertices(0, i));
+ max_x = std::max(max_x, transf_vertices(0, i));
+ min_y = std::min(min_y, transf_vertices(1, i));
+ max_y = std::max(max_y, transf_vertices(1, i));
+ min_z = std::min(min_z, transf_vertices(2, i));
+ max_z = std::max(max_z, transf_vertices(2, i));
+ }
+
+ return BoundingBoxf3(Pointf3((coordf_t)min_x, (coordf_t)min_y, (coordf_t)min_z), Pointf3((coordf_t)max_x, (coordf_t)max_y, (coordf_t)max_z));
+}
+
}
diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp
index 5de94aa9c..1f71536ee 100644
--- a/xs/src/libslic3r/BoundingBox.hpp
+++ b/xs/src/libslic3r/BoundingBox.hpp
@@ -148,6 +148,8 @@ public:
BoundingBoxf3() : BoundingBox3Base<Pointf3>() {};
BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base<Pointf3>(pmin, pmax) {};
BoundingBoxf3(const std::vector<Pointf3> &points) : BoundingBox3Base<Pointf3>(points) {};
+
+ BoundingBoxf3 transformed(const std::vector<float>& matrix) const;
};
template<typename VT>
diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index 16ef51c1f..15363e8ed 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -92,6 +92,7 @@ public:
virtual double min_mm3_per_mm() const = 0;
virtual Polyline as_polyline() const = 0;
virtual double length() const = 0;
+ virtual double total_volume() const = 0;
};
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
@@ -148,6 +149,7 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const { return this->mm3_per_mm; }
Polyline as_polyline() const { return this->polyline; }
+ virtual double total_volume() const { return mm3_per_mm * unscale(length()); }
private:
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
@@ -194,6 +196,7 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const;
+ virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
};
// Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
@@ -241,6 +244,7 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
+ virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
private:
ExtrusionLoopRole m_loop_role;
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp
index 4513139e2..7a086bcbf 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp
@@ -125,6 +125,7 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
continue;
}
}
+
ExtrusionEntity* entity = (*it)->clone();
my_paths.push_back(entity);
if (orig_indices != NULL) indices_map[entity] = it - this->entities.begin();
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
index 03bd2ba97..382455fe3 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -79,6 +79,7 @@ public:
void flatten(ExtrusionEntityCollection* retval) const;
ExtrusionEntityCollection flatten() const;
double min_mm3_per_mm() const;
+ virtual double total_volume() const {double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
// Following methods shall never be called on an ExtrusionEntityCollection.
Polyline as_polyline() const {
diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp
index 263363756..21d4b4d3b 100644
--- a/xs/src/libslic3r/Format/AMF.cpp
+++ b/xs/src/libslic3r/Format/AMF.cpp
@@ -13,9 +13,7 @@
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string.hpp>
-//############################################################################################################################################
#include <boost/nowide/fstream.hpp>
-//############################################################################################################################################
#include <miniz/miniz_zip.h>
#if 0
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index be87e12b9..e587c8e93 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -808,7 +808,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
}
// Extrude the layers.
for (auto &layer : layers_to_print) {
- const ToolOrdering::LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
+ const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
if (m_wipe_tower && layer_tools.has_wipe_tower)
m_wipe_tower->next_layer();
this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
@@ -1058,7 +1058,7 @@ void GCode::process_layer(
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers,
- const ToolOrdering::LayerTools &layer_tools,
+ const LayerTools &layer_tools,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
const size_t single_object_idx)
@@ -1196,7 +1196,6 @@ void GCode::process_layer(
// Group extrusions by an extruder, then by an object, an island and a region.
std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder;
-
for (const LayerToPrint &layer_to_print : layers) {
if (layer_to_print.support_layer != nullptr) {
const SupportLayer &support_layer = *layer_to_print.support_layer;
@@ -1273,70 +1272,66 @@ void GCode::process_layer(
if (layerm == nullptr)
continue;
const PrintRegion &region = *print.regions[region_id];
-
- // process perimeters
- for (const ExtrusionEntity *ee : layerm->perimeters.entities) {
- // perimeter_coll represents perimeter extrusions of a single island.
- const auto *perimeter_coll = dynamic_cast<const ExtrusionEntityCollection*>(ee);
- if (perimeter_coll->entities.empty())
- // This shouldn't happen but first_point() would fail.
- continue;
- // Init by_extruder item only if we actually use the extruder.
- std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
- by_extruder,
- std::max<int>(region.config.perimeter_extruder.value - 1, 0),
- &layer_to_print - layers.data(),
- layers.size(), n_slices+1);
- for (size_t i = 0; i <= n_slices; ++ i)
- if (// perimeter_coll->first_point does not fit inside any slice
- i == n_slices ||
- // perimeter_coll->first_point fits inside ith slice
- point_inside_surface(i, perimeter_coll->first_point())) {
- if (islands[i].by_region.empty())
- islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
- islands[i].by_region[region_id].perimeters.append(perimeter_coll->entities);
- break;
- }
- }
-
- // process infill
- // layerm->fills is a collection of Slic3r::ExtrusionPath::Collection objects (C++ class ExtrusionEntityCollection),
- // each one containing the ExtrusionPath objects of a certain infill "group" (also called "surface"
- // throughout the code). We can redefine the order of such Collections but we have to
- // do each one completely at once.
- for (const ExtrusionEntity *ee : layerm->fills.entities) {
- // fill represents infill extrusions of a single island.
- const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
- if (fill->entities.empty())
- // This shouldn't happen but first_point() would fail.
- continue;
- // init by_extruder item only if we actually use the extruder
- int extruder_id = std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1);
- // Init by_extruder item only if we actually use the extruder.
- std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
- by_extruder,
- extruder_id,
- &layer_to_print - layers.data(),
- layers.size(), n_slices+1);
- for (size_t i = 0; i <= n_slices; ++i)
- if (// fill->first_point does not fit inside any slice
- i == n_slices ||
- // fill->first_point fits inside ith slice
- point_inside_surface(i, fill->first_point())) {
- if (islands[i].by_region.empty())
- islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
- islands[i].by_region[region_id].infills.append(fill->entities);
- break;
+
+
+ // Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
+ // It is also necessary to save which extrusions are part of MM wiping and which are not.
+ // The process is almost the same for perimeters and infills - we will do it in a cycle that repeats twice:
+ for (std::string entity_type("infills") ; entity_type != "done" ; entity_type = entity_type=="infills" ? "perimeters" : "done") {
+
+ const ExtrusionEntitiesPtr& source_entities = entity_type=="infills" ? layerm->fills.entities : layerm->perimeters.entities;
+
+ for (const ExtrusionEntity *ee : source_entities) {
+ // fill represents infill extrusions of a single island.
+ const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (fill->entities.empty()) // This shouldn't happen but first_point() would fail.
+ continue;
+
+ // This extrusion is part of certain Region, which tells us which extruder should be used for it:
+ int correct_extruder_id = Print::get_extruder(*fill, region); entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
+ std::max<int>(region.config.perimeter_extruder.value - 1, 0);
+
+ // Let's recover vector of extruder overrides:
+ const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
+
+ // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
+ for (unsigned int extruder : layer_tools.extruders)
+ {
+ // Init by_extruder item only if we actually use the extruder:
+ if (std::find(entity_overrides->begin(), entity_overrides->end(), extruder) != entity_overrides->end() || // at least one copy is overridden to use this extruder
+ std::find(entity_overrides->begin(), entity_overrides->end(), -extruder-1) != entity_overrides->end() || // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
+ (std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder_id) == layer_tools.extruders.end() && extruder == layer_tools.extruders.back())) // this entity is not overridden, but its extruder is not in layer_tools - we'll print it
+ //by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools)
+ {
+ std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
+ by_extruder,
+ extruder,
+ &layer_to_print - layers.data(),
+ layers.size(), n_slices+1);
+ for (size_t i = 0; i <= n_slices; ++i)
+ if (// fill->first_point does not fit inside any slice
+ i == n_slices ||
+ // fill->first_point fits inside ith slice
+ point_inside_surface(i, fill->first_point())) {
+ if (islands[i].by_region.empty())
+ islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
+ islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->_shifted_copies.size());
+ break;
+ }
+ }
}
+ }
}
} // for regions
}
} // for objects
+
+
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
for (unsigned int extruder_id : layer_tools.extruders)
- {
+ {
gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ?
m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) :
this->set_extruder(extruder_id);
@@ -1361,7 +1356,7 @@ void GCode::process_layer(
for (ExtrusionPath &path : loop.paths) {
path.height = (float)layer.height;
path.mm3_per_mm = mm3_per_mm;
- }
+ }
gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value);
}
m_avoid_crossing_perimeters.use_external_mp = false;
@@ -1370,7 +1365,7 @@ void GCode::process_layer(
m_avoid_crossing_perimeters.disable_once = true;
}
}
-
+
// Extrude brim with the extruder of the 1st region.
if (! m_brim_done) {
this->set_origin(0., 0.);
@@ -1383,49 +1378,61 @@ void GCode::process_layer(
m_avoid_crossing_perimeters.disable_once = true;
}
+
auto objects_by_extruder_it = by_extruder.find(extruder_id);
if (objects_by_extruder_it == by_extruder.end())
continue;
- for (const ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
- const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data();
- const PrintObject *print_object = layers[layer_id].object();
- if (print_object == nullptr)
- // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
- continue;
-
- m_config.apply(print_object->config, true);
- m_layer = layers[layer_id].layer();
- if (m_config.avoid_crossing_perimeters)
- m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
- Points copies;
- if (single_object_idx == size_t(-1))
- copies = print_object->_shifted_copies;
- else
- copies.push_back(print_object->_shifted_copies[single_object_idx]);
- // Sort the copies by the closest point starting with the current print position.
-
- for (const Point &copy : copies) {
- // When starting a new object, use the external motion planner for the first travel move.
- std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
- if (m_last_obj_copy != this_object_copy)
- m_avoid_crossing_perimeters.use_external_mp_once = true;
- m_last_obj_copy = this_object_copy;
- this->set_origin(unscale(copy.x), unscale(copy.y));
- if (object_by_extruder.support != nullptr) {
- m_layer = layers[layer_id].support_layer;
- gcode += this->extrude_support(
- // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
- object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
- m_layer = layers[layer_id].layer();
- }
- for (const ObjectByExtruder::Island &island : object_by_extruder.islands) {
- if (print.config.infill_first) {
- gcode += this->extrude_infill(print, island.by_region);
- gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
- } else {
- gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
- gcode += this->extrude_infill(print, island.by_region);
+
+ // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
+ for (int print_wipe_extrusions=const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
+ if (print_wipe_extrusions == 0)
+ gcode+="; PURGING FINISHED\n";
+
+ for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
+ const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data();
+ const PrintObject *print_object = layers[layer_id].object();
+ if (print_object == nullptr)
+ // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
+ continue;
+
+ m_config.apply(print_object->config, true);
+ m_layer = layers[layer_id].layer();
+ if (m_config.avoid_crossing_perimeters)
+ m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
+ Points copies;
+ if (single_object_idx == size_t(-1))
+ copies = print_object->_shifted_copies;
+ else
+ copies.push_back(print_object->_shifted_copies[single_object_idx]);
+ // Sort the copies by the closest point starting with the current print position.
+
+ unsigned int copy_id = 0;
+ for (const Point &copy : copies) {
+ // When starting a new object, use the external motion planner for the first travel move.
+ std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
+ if (m_last_obj_copy != this_object_copy)
+ m_avoid_crossing_perimeters.use_external_mp_once = true;
+ m_last_obj_copy = this_object_copy;
+ this->set_origin(unscale(copy.x), unscale(copy.y));
+ if (object_by_extruder.support != nullptr && !print_wipe_extrusions) {
+ m_layer = layers[layer_id].support_layer;
+ gcode += this->extrude_support(
+ // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
+ object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
+ m_layer = layers[layer_id].layer();
}
+ for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
+ const auto& by_region_specific = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
+
+ if (print.config.infill_first) {
+ gcode += this->extrude_infill(print, by_region_specific);
+ gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
+ } else {
+ gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
+ gcode += this->extrude_infill(print,by_region_specific);
+ }
+ }
+ ++copy_id;
}
}
}
@@ -2496,4 +2503,62 @@ Point GCode::gcode_to_point(const Pointf &point) const
scale_(point.y - m_origin.y + extruder_offset.y));
}
+
+// Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed
+// during infill/perimeter wiping, or normally (depends on wiping_entities parameter)
+// Returns a reference to member to avoid copying.
+const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities)
+{
+ by_region_per_copy_cache.clear();
+
+ for (const auto& reg : by_region) {
+ by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region()); // creates a region in the newly created Island
+
+ // Now we are going to iterate through perimeters and infills and pick ones that are supposed to be printed
+ // References are used so that we don't have to repeat the same code
+ for (int iter = 0; iter < 2; ++iter) {
+ const ExtrusionEntitiesPtr& entities = (iter ? reg.infills.entities : reg.perimeters.entities);
+ ExtrusionEntityCollection& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters);
+ const std::vector<const ExtruderPerCopy*>& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides);
+
+ // Now the most important thing - which extrusion should we print.
+ // See function ToolOrdering::get_extruder_overrides for details about the negative numbers hack.
+ int this_extruder_mark = wiping_entities ? extruder : -extruder-1;
+
+ for (unsigned int i=0;i<entities.size();++i)
+ if (overrides[i]->at(copy) == this_extruder_mark) // this copy should be printed with this extruder
+ target_eec.append((*entities[i]));
+ }
+ }
+ return by_region_per_copy_cache;
}
+
+
+
+// This function takes the eec and appends its entities to either perimeters or infills of this Region (depending on the first parameter)
+// It also saves pointer to ExtruderPerCopy struct (for each entity), that holds information about which extruders should be used for which copy.
+void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, unsigned int object_copies_num)
+{
+ // We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves:
+ ExtrusionEntityCollection* perimeters_or_infills = &infills;
+ std::vector<const ExtruderPerCopy*>* perimeters_or_infills_overrides = &infills_overrides;
+
+ if (type == "perimeters") {
+ perimeters_or_infills = &perimeters;
+ perimeters_or_infills_overrides = &perimeters_overrides;
+ }
+ else
+ if (type != "infills") {
+ CONFESS("Unknown parameter!");
+ return;
+ }
+
+
+ // First we append the entities, there are eec->entities.size() of them:
+ perimeters_or_infills->append(eec->entities);
+
+ for (unsigned int i=0;i<eec->entities.size();++i)
+ perimeters_or_infills_overrides->push_back(copies_extruder);
+}
+
+} // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index 163ade82f..7bc525731 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -188,7 +188,7 @@ protected:
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers,
- const ToolOrdering::LayerTools &layer_tools,
+ const LayerTools &layer_tools,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
const size_t single_object_idx = size_t(-1));
@@ -203,6 +203,7 @@ protected:
std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.);
std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.);
+ typedef std::vector<int> ExtruderPerCopy;
// Extruding multiple objects with soluble / non-soluble / combined supports
// on a multi-material printer, trying to minimize tool switches.
// Following structures sort extrusions by the extruder ID, by an order of objects and object islands.
@@ -218,11 +219,24 @@ protected:
struct Region {
ExtrusionEntityCollection perimeters;
ExtrusionEntityCollection infills;
+
+ std::vector<const ExtruderPerCopy*> infills_overrides;
+ std::vector<const ExtruderPerCopy*> perimeters_overrides;
+
+ // Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping
+ void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, unsigned int object_copies_num);
};
- std::vector<Region> by_region;
+
+ std::vector<Region> by_region; // all extrusions for this island, grouped by regions
+ const std::vector<Region>& by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities = false); // returns reference to subvector of by_region
+
+ private:
+ std::vector<Region> by_region_per_copy_cache; // caches vector generated by function above to avoid copying and recalculating
};
std::vector<Island> islands;
};
+
+
std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);
std::string extrude_support(const ExtrusionEntityCollection &support_fills);
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 271b75ef3..f1dbbfc1e 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -15,6 +15,24 @@
namespace Slic3r {
+
+// Returns true in case that extruder a comes before b (b does not have to be present). False otherwise.
+bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
+{
+ if (a==b)
+ return false;
+
+ for (auto extruder : extruders) {
+ if (extruder == a)
+ return true;
+ if (extruder == b)
+ return false;
+ }
+
+ return false;
+}
+
+
// For the use case when each object is printed separately
// (print.config.complete_objects is true).
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
@@ -48,6 +66,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
// (print.config.complete_objects is false).
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
{
+ m_print_config_ptr = &print.config;
// Initialize the print layers for all objects and all layers.
coordf_t object_bottom_z = 0.;
{
@@ -76,9 +95,10 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
this->collect_extruder_statistics(prime_multi_material);
}
-ToolOrdering::LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z)
+
+LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z)
{
- auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), ToolOrdering::LayerTools(print_z - EPSILON));
+ auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), LayerTools(print_z - EPSILON));
assert(it_layer_tools != m_layer_tools.end());
coordf_t dist_min = std::abs(it_layer_tools->print_z - print_z);
for (++ it_layer_tools; it_layer_tools != m_layer_tools.end(); ++it_layer_tools) {
@@ -102,7 +122,7 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
coordf_t zmax = zs[i] + EPSILON;
for (; j < zs.size() && zs[j] <= zmax; ++ j) ;
// Assign an average print_z to the set of layers with nearly equal print_z.
- m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1])));
+ m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1]), m_print_config_ptr));
i = j;
}
}
@@ -134,12 +154,29 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (layerm == nullptr)
continue;
const PrintRegion &region = *object.print()->regions[region_id];
+
if (! layerm->perimeters.entities.empty()) {
- layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
+ bool something_nonoverriddable = true;
+
+ if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
+ something_nonoverriddable = false;
+ for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
+ if (!layer_tools.wiping_extrusions().is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
+ something_nonoverriddable = true;
+ break;
+ }
+ }
+
+ if (something_nonoverriddable)
+ layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
+
layer_tools.has_object = true;
}
+
+
bool has_infill = false;
bool has_solid_infill = false;
+ bool something_nonoverriddable = false;
for (const ExtrusionEntity *ee : layerm->fills.entities) {
// fill represents infill extrusions of a single island.
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
@@ -148,19 +185,33 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
has_solid_infill = true;
else if (role != erNone)
has_infill = true;
+
+ if (m_print_config_ptr) {
+ if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable(*fill, *m_print_config_ptr, object, region))
+ something_nonoverriddable = true;
+ }
+ }
+
+ if (something_nonoverriddable || !m_print_config_ptr)
+ {
+ if (has_solid_infill)
+ layer_tools.extruders.push_back(region.config.solid_infill_extruder);
+ if (has_infill)
+ layer_tools.extruders.push_back(region.config.infill_extruder);
}
- if (has_solid_infill)
- layer_tools.extruders.push_back(region.config.solid_infill_extruder);
- if (has_infill)
- layer_tools.extruders.push_back(region.config.infill_extruder);
if (has_solid_infill || has_infill)
layer_tools.has_object = true;
}
}
- // Sort and remove duplicates
- for (LayerTools &lt : m_layer_tools)
- sort_remove_duplicates(lt.extruders);
+ for (auto& layer : m_layer_tools) {
+ // Sort and remove duplicates
+ sort_remove_duplicates(layer.extruders);
+
+ // make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
+ if (layer.extruders.empty() && layer.has_object)
+ layer.extruders.push_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders
+ }
}
// Reorder extruders to minimize layer changes.
@@ -217,6 +268,8 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
}
}
+
+
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z)
{
if (m_layer_tools.empty())
@@ -327,4 +380,250 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
}
}
+
+
+// This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual)
+void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies)
+{
+ something_overridden = true;
+
+ auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; // (add and) return iterator
+ auto& copies_vector = entity_map_it->second;
+ if (copies_vector.size() < num_of_copies)
+ copies_vector.resize(num_of_copies, -1);
+
+ if (copies_vector[copy_id] != -1)
+ std::cout << "ERROR: Entity extruder overriden multiple times!!!\n"; // A debugging message - this must never happen.
+
+ copies_vector[copy_id] = extruder;
+}
+
+
+// Finds first non-soluble extruder on the layer
+int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
+{
+ const LayerTools& lt = *m_layer_tools;
+ for (auto extruders_it = lt.extruders.begin(); extruders_it != lt.extruders.end(); ++extruders_it)
+ if (!print_config.filament_soluble.get_at(*extruders_it))
+ return (*extruders_it);
+
+ return (-1);
+}
+
+// Finds last non-soluble extruder on the layer
+int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
+{
+ const LayerTools& lt = *m_layer_tools;
+ for (auto extruders_it = lt.extruders.rbegin(); extruders_it != lt.extruders.rend(); ++extruders_it)
+ if (!print_config.filament_soluble.get_at(*extruders_it))
+ return (*extruders_it);
+
+ return (-1);
+}
+
+
+// Decides whether this entity could be overridden
+bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const
+{
+ if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region)))
+ return false;
+
+ if (object.config.wipe_into_objects)
+ return true;
+
+ if (!region.config.wipe_into_infill || eec.role() != erInternalInfill)
+ return false;
+
+ return true;
+}
+
+
+// Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange
+// and returns volume that is left to be wiped on the wipe tower.
+float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int new_extruder, float volume_to_wipe)
+{
+ const LayerTools& lt = *m_layer_tools;
+ const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
+
+ if (print.config.filament_soluble.get_at(new_extruder))
+ return volume_to_wipe; // Soluble filament cannot be wiped in a random infill
+
+ // we will sort objects so that dedicated for wiping are at the beginning:
+ PrintObjectPtrs object_list = print.objects;
+ std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
+
+
+ // We will now iterate through
+ // - first the dedicated objects to mark perimeters or infills (depending on infill_first)
+ // - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
+ // - then all the others to mark infills (in case that !infill_first, we must also check that the perimeter is finished already
+ // this is controlled by the following variable:
+ bool perimeters_done = false;
+
+ for (int i=0 ; i<(int)object_list.size() + (perimeters_done ? 0 : 1); ++i) {
+ if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
+ perimeters_done = true;
+ i=-1; // let's go from the start again
+ continue;
+ }
+
+ const auto& object = object_list[i];
+
+ // Finds this layer:
+ auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
+ if (this_layer_it == object->layers.end())
+ continue;
+ const Layer* this_layer = *this_layer_it;
+ unsigned int num_of_copies = object->_shifted_copies.size();
+
+ for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
+
+ for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
+ const auto& region = *object->print()->regions[region_id];
+
+ if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
+ continue;
+
+
+ if ((!print.config.infill_first ? perimeters_done : !perimeters_done) || (!object->config.wipe_into_objects && region.config.wipe_into_infill)) {
+ for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+
+ if (!is_overriddable(*fill, print.config, *object, region))
+ continue;
+
+ // What extruder would this normally be printed with?
+ unsigned int correct_extruder = Print::get_extruder(*fill, region);
+
+ if (volume_to_wipe<=0)
+ continue;
+
+ if (!object->config.wipe_into_objects && !print.config.infill_first && region.config.wipe_into_infill)
+ // In this case we must check that the original extruder is used on this layer before the one we are overridding
+ // (and the perimeters will be finished before the infill is printed):
+ if (!lt.is_extruder_order(region.config.perimeter_extruder - 1, new_extruder))
+ continue;
+
+ if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
+ set_extruder_override(fill, copy, new_extruder, num_of_copies);
+ volume_to_wipe -= fill->total_volume();
+ }
+ }
+ }
+
+ // Now the same for perimeters - see comments above for explanation:
+ if (object->config.wipe_into_objects && (print.config.infill_first ? perimeters_done : !perimeters_done))
+ {
+ for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) {
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (!is_overriddable(*fill, print.config, *object, region))
+ continue;
+
+ if (volume_to_wipe<=0)
+ continue;
+
+ if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
+ set_extruder_override(fill, copy, new_extruder, num_of_copies);
+ volume_to_wipe -= fill->total_volume();
+ }
+ }
+ }
+ }
+ }
+ }
+ return std::max(0.f, volume_to_wipe);
+}
+
+
+
+// Called after all toolchanges on a layer were mark_infill_overridden. There might still be overridable entities,
+// that were not actually overridden. If they are part of a dedicated object, printing them with the extruder
+// they were initially assigned to might mean violating the perimeter-infill order. We will therefore go through
+// them again and make sure we override it.
+void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
+{
+ const LayerTools& lt = *m_layer_tools;
+ unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config);
+ unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config);
+
+ for (const PrintObject* object : print.objects) {
+ // Finds this layer:
+ auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
+ if (this_layer_it == object->layers.end())
+ continue;
+ const Layer* this_layer = *this_layer_it;
+ unsigned int num_of_copies = object->_shifted_copies.size();
+
+ for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
+ for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
+ const auto& region = *object->print()->regions[region_id];
+
+ if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
+ continue;
+
+ for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+
+ if (!is_overriddable(*fill, print.config, *object, region)
+ || is_entity_overridden(fill, copy) )
+ continue;
+
+ // This infill could have been overridden but was not - unless we do something, it could be
+ // printed before its perimeter, or not be printed at all (in case its original extruder has
+ // not been added to LayerTools
+ // Either way, we will now force-override it with something suitable:
+ if (print.config.infill_first
+ || object->config.wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
+ || lt.is_extruder_order(region.config.perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
+ || std::find(lt.extruders.begin(), lt.extruders.end(), region.config.infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
+ )
+ set_extruder_override(fill, copy, (print.config.infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
+ else {
+ // In this case we can (and should) leave it to be printed normally.
+ // Force overriding would mean it gets printed before its perimeter.
+ }
+ }
+
+ // Now the same for perimeters - see comments above for explanation:
+ for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) { // iterate through all perimeter Collections
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (!is_overriddable(*fill, print.config, *object, region)
+ || is_entity_overridden(fill, copy) )
+ continue;
+
+ set_extruder_override(fill, copy, (print.config.infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+// Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
+// It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
+// It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
+// so -1 was used as "print as usual".
+// The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
+// its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
+const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies)
+{
+ auto entity_map_it = entity_map.find(entity);
+ if (entity_map_it == entity_map.end())
+ entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first;
+
+ // Now the entity_map_it should be valid, let's make sure the vector is long enough:
+ entity_map_it->second.resize(num_of_copies, -1);
+
+ // Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
+ std::replace(entity_map_it->second.begin(), entity_map_it->second.end(), -1, -correct_extruder_id-1);
+
+ return &(entity_map_it->second);
+}
+
+
} // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp
index c92806b19..13e0212f1 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp
@@ -9,38 +9,97 @@ namespace Slic3r {
class Print;
class PrintObject;
+class LayerTools;
-class ToolOrdering
+
+
+// Object of this class holds information about whether an extrusion is printed immediately
+// after a toolchange (as part of infill/perimeter wiping) or not. One extrusion can be a part
+// of several copies - this has to be taken into account.
+class WipingExtrusions
+{
+public:
+ bool is_anything_overridden() const { // if there are no overrides, all the agenda can be skipped - this function can tell us if that's the case
+ return something_overridden;
+ }
+
+ // This is called from GCode::process_layer - see implementation for further comments:
+ const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies);
+
+ // This function goes through all infill entities, decides which ones will be used for wiping and
+ // marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
+ float mark_wiping_extrusions(const Print& print, unsigned int new_extruder, float volume_to_wipe);
+
+ void ensure_perimeters_infills_order(const Print& print);
+
+ bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const;
+
+ void set_layer_tools_ptr(const LayerTools* lt) { m_layer_tools = lt; }
+
+private:
+ int first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
+ int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
+
+ // This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
+ void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
+
+ // Returns true in case that entity is not printed with its usual extruder for a given copy:
+ bool is_entity_overridden(const ExtrusionEntity* entity, int copy_id) const {
+ return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1);
+ }
+
+ std::map<const ExtrusionEntity*, std::vector<int>> entity_map; // to keep track of who prints what
+ bool something_overridden = false;
+ const LayerTools* m_layer_tools; // so we know which LayerTools object this belongs to
+};
+
+
+
+class LayerTools
{
public:
- struct LayerTools
- {
- LayerTools(const coordf_t z) :
- print_z(z),
- has_object(false),
- has_support(false),
- has_wipe_tower(false),
- wipe_tower_partitions(0),
- wipe_tower_layer_height(0.) {}
-
- bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
- bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
-
- coordf_t print_z;
- bool has_object;
- bool has_support;
- // Zero based extruder IDs, ordered to minimize tool switches.
- std::vector<unsigned int> extruders;
- // Will there be anything extruded on this layer for the wipe tower?
- // Due to the support layers possibly interleaving the object layers,
- // wipe tower will be disabled for some support only layers.
- bool has_wipe_tower;
- // Number of wipe tower partitions to support the required number of tool switches
- // and to support the wipe tower partitions above this one.
- size_t wipe_tower_partitions;
- coordf_t wipe_tower_layer_height;
- };
+ LayerTools(const coordf_t z, const PrintConfig* print_config_ptr = nullptr) :
+ print_z(z),
+ has_object(false),
+ has_support(false),
+ has_wipe_tower(false),
+ wipe_tower_partitions(0),
+ wipe_tower_layer_height(0.) {}
+
+ bool operator< (const LayerTools &rhs) const { return print_z - EPSILON < rhs.print_z; }
+ bool operator==(const LayerTools &rhs) const { return std::abs(print_z - rhs.print_z) < EPSILON; }
+
+ bool is_extruder_order(unsigned int a, unsigned int b) const;
+
+ coordf_t print_z;
+ bool has_object;
+ bool has_support;
+ // Zero based extruder IDs, ordered to minimize tool switches.
+ std::vector<unsigned int> extruders;
+ // Will there be anything extruded on this layer for the wipe tower?
+ // Due to the support layers possibly interleaving the object layers,
+ // wipe tower will be disabled for some support only layers.
+ bool has_wipe_tower;
+ // Number of wipe tower partitions to support the required number of tool switches
+ // and to support the wipe tower partitions above this one.
+ size_t wipe_tower_partitions;
+ coordf_t wipe_tower_layer_height;
+
+ WipingExtrusions& wiping_extrusions() {
+ m_wiping_extrusions.set_layer_tools_ptr(this);
+ return m_wiping_extrusions;
+ }
+
+private:
+ // This object holds list of extrusion that will be used for extruder wiping
+ WipingExtrusions m_wiping_extrusions;
+};
+
+
+class ToolOrdering
+{
+public:
ToolOrdering() {}
// For the use case when each object is printed separately
@@ -72,7 +131,7 @@ public:
std::vector<LayerTools>::const_iterator begin() const { return m_layer_tools.begin(); }
std::vector<LayerTools>::const_iterator end() const { return m_layer_tools.end(); }
bool empty() const { return m_layer_tools.empty(); }
- const std::vector<LayerTools>& layer_tools() const { return m_layer_tools; }
+ std::vector<LayerTools>& layer_tools() { return m_layer_tools; }
bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; }
private:
@@ -80,17 +139,22 @@ private:
void collect_extruders(const PrintObject &object);
void reorder_extruders(unsigned int last_extruder_id);
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
- void collect_extruder_statistics(bool prime_multi_material);
-
- std::vector<LayerTools> m_layer_tools;
- // First printing extruder, including the multi-material priming sequence.
- unsigned int m_first_printing_extruder = (unsigned int)-1;
- // Final printing extruder.
- unsigned int m_last_printing_extruder = (unsigned int)-1;
- // All extruders, which extrude some material over m_layer_tools.
- std::vector<unsigned int> m_all_printing_extruders;
+ void collect_extruder_statistics(bool prime_multi_material);
+
+ std::vector<LayerTools> m_layer_tools;
+ // First printing extruder, including the multi-material priming sequence.
+ unsigned int m_first_printing_extruder = (unsigned int)-1;
+ // Final printing extruder.
+ unsigned int m_last_printing_extruder = (unsigned int)-1;
+ // All extruders, which extrude some material over m_layer_tools.
+ std::vector<unsigned int> m_all_printing_extruders;
+
+
+ const PrintConfig* m_print_config_ptr = nullptr;
};
+
+
} // namespace SLic3r
#endif /* slic3r_ToolOrdering_hpp_ */
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 5aa6470a2..b49c2856b 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -21,7 +21,6 @@ TODO LIST
#include <iostream>
#include <vector>
#include <numeric>
-#include <algorithm>
#include "Analyzer.hpp"
@@ -138,7 +137,7 @@ public:
width += m_layer_height * float(1. - M_PI / 4.);
if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos)
m_extrusions.emplace_back(WipeTower::Extrusion(rotated_current_pos, 0, m_current_tool));
- m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool));
+ m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool));
}
m_gcode += "G1";
@@ -231,6 +230,17 @@ public:
Writer& retract(float e, float f = 0.f)
{ return load(-e, f); }
+// Loads filament while also moving towards given points in x-axis (x feedrate is limited by cutting the distance short if necessary)
+ Writer& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f)
+ {
+ float time = std::abs(loading_dist / loading_speed);
+ float x_speed = std::min(max_x_speed, std::abs(farthest_x - x()) / time);
+ float feedrate = 60.f * std::hypot(x_speed, loading_speed);
+
+ float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_speed * time;
+ return extrude_explicit(end_point, y(), loading_dist, feedrate);
+ }
+
// Elevate the extruder head above the current print_z position.
Writer& z_hop(float hop, float f = 0.f)
{
@@ -276,12 +286,9 @@ public:
// Set extruder temperature, don't wait by default.
Writer& set_extruder_temp(int temperature, bool wait = false)
{
- if (temperature != current_temp) {
- char buf[128];
- sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
- m_gcode += buf;
- current_temp = temperature;
- }
+ char buf[128];
+ sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
+ m_gcode += buf;
return *this;
};
@@ -399,8 +406,7 @@ private:
int current_temp = -1;
const float m_default_analyzer_line_width;
- std::string
- set_format_X(float x)
+ std::string set_format_X(float x)
{
char buf[64];
sprintf(buf, " X%.3f", x);
@@ -475,7 +481,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
bool last_wipe_inside_wipe_tower)
{
-
this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
this->m_current_tool = tools.front();
@@ -558,7 +563,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
{
for (const auto &b : m_layer_info->tool_changes)
if ( b.new_tool == tool ) {
- wipe_volume = wipe_volumes[b.old_tool][b.new_tool];
+ wipe_volume = b.wipe_volume;
if (tool == m_layer_info->tool_changes.back().new_tool)
last_change_in_layer = true;
wipe_area = b.required_depth * m_layer_info->extra_spacing;
@@ -783,51 +788,44 @@ void WipeTowerPrusaMM::toolchange_Unload(
WipeTower::xy end_of_ramming(writer.x(),writer.y());
writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
- // Pull the filament end to the BEGINNING of the cooling tube while still moving the print head
- float oldx = writer.x();
- float turning_point = (!m_left_to_right ? std::max(xl,oldx-15.f) : std::min(xr,oldx+15.f) ); // so it's not too far
- float xdist = std::abs(oldx-turning_point);
- float edist = -(m_cooling_tube_retraction+m_cooling_tube_length/2.f-42);
-
+ // Retraction:
+ float old_x = writer.x();
+ float turning_point = (!m_left_to_right ? xl : xr );
+ float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
writer.suppress_preview()
- .load_move_x(turning_point,-15 , 60.f * std::hypot(xdist,15)/15 * 83 ) // fixed speed after ramming
- .load_move_x(oldx ,edist , 60.f * std::hypot(xdist,edist)/std::abs(edist) * m_filpar[m_current_tool].unloading_speed )
- .load_move_x(turning_point,-15 , 60.f * std::hypot(xdist,15)/15 * m_filpar[m_current_tool].unloading_speed*0.55f )
- .load_move_x(oldx ,-12 , 60.f * std::hypot(xdist,12)/12 * m_filpar[m_current_tool].unloading_speed*0.35f )
+ .load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
+ .load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed)
+ .load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed)
+ .load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
+ .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate
.resume_preview();
- if (new_temperature != 0) // Set the extruder temperature, but don't wait.
+ if (new_temperature != 0 && new_temperature != m_old_temperature ) { // Set the extruder temperature, but don't wait.
writer.set_extruder_temp(new_temperature, false);
+ m_old_temperature = new_temperature;
+ }
-// cooling:
- writer.suppress_preview();
- writer.travel(writer.x(), writer.y() + y_step);
- const float start_x = writer.x();
- turning_point = ( xr-start_x > start_x-xl ? xr : xl );
- const float max_x_dist = 2*std::abs(start_x-turning_point);
- const unsigned int N = 4 + std::max(0.f, (m_filpar[m_current_tool].cooling_time-14)/3);
- float time = m_filpar[m_current_tool].cooling_time / float(N);
-
- i = 0;
- while (i<N) {
- const float speed = std::min(3.4,2.2 + i*0.3 + (i==0 ? 0 : 0.3)); // mm per second: 2.2, 2.8, 3.1, 3.4, 3.4, 3.4, ...
- const float e_dist = std::min(speed * time,2*m_cooling_tube_length); // distance to travel
-
- // this move is the last one at this speed or someone set tube_length to zero
- if (speed * time < 2*m_cooling_tube_length || m_cooling_tube_length<WT_EPSILON) {
- ++i;
- time = m_filpar[m_current_tool].cooling_time / float(N);
- }
- else
- time -= e_dist / speed; // subtract time this part will really take
-
- // as for x, we will make sure the feedrate is at most 2000
- float x_dist = (turning_point - WT_EPSILON < xl ? -1.f : 1.f) * std::min(e_dist * (float)sqrt(pow(2000 / (60 * speed), 2) - 1),max_x_dist);
- const float feedrate = std::hypot(e_dist, x_dist) / ((e_dist / speed) / 60.f);
- writer.cool(start_x+x_dist/2.f,start_x,e_dist/2.f,-e_dist/2.f, feedrate);
- }
+ // Cooling:
+ const int& number_of_moves = m_filpar[m_current_tool].cooling_moves;
+ if (number_of_moves > 0) {
+ const float& initial_speed = m_filpar[m_current_tool].cooling_initial_speed;
+ const float& final_speed = m_filpar[m_current_tool].cooling_final_speed;
+
+ float speed_inc = (final_speed - initial_speed) / (2.f * number_of_moves - 1.f);
+
+ writer.suppress_preview()
+ .travel(writer.x(), writer.y() + y_step);
+ old_x = writer.x();
+ turning_point = xr-old_x > old_x-xl ? xr : xl;
+ for (int i=0; i<number_of_moves; ++i) {
+ float speed = initial_speed + speed_inc * 2*i;
+ writer.load_move_x_advanced(turning_point, m_cooling_tube_length, speed);
+ speed += speed_inc;
+ writer.load_move_x_advanced(old_x, -m_cooling_tube_length, speed);
+ }
+ }
- // let's wait is necessary
+ // let's wait is necessary:
writer.wait(m_filpar[m_current_tool].delay);
// we should be at the beginning of the cooling tube again - let's move to parking position:
writer.retract(-m_cooling_tube_length/2.f+m_parking_pos_retraction-m_cooling_tube_retraction, 2000);
@@ -871,16 +869,16 @@ void WipeTowerPrusaMM::toolchange_Load(
float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position
// Load the filament while moving left / right, so the excess material will not create a blob at a single position.
- float loading_speed = m_filpar[m_current_tool].loading_speed; // mm/s in e axis
float turning_point = ( oldx-xl < xr-oldx ? xr : xl );
- float dist = std::abs(oldx-turning_point);
- float edist = m_parking_pos_retraction-50-2; // loading is 2mm shorter that previous retraction, 50mm reserved for acceleration/deceleration
- writer.append("; CP TOOLCHANGE LOAD\n")
+ float edist = m_parking_pos_retraction+m_extra_loading_move;
+
+ writer.append("; CP TOOLCHANGE LOAD\n")
.suppress_preview()
- .load_move_x(turning_point, 20, 60*std::hypot(dist,20.f)/20.f * loading_speed*0.3f) // Acceleration
- .load_move_x(oldx,edist,60*std::hypot(dist,edist)/edist * loading_speed) // Fast phase
- .load_move_x(turning_point, 20, 60*std::hypot(dist,20.f)/20.f * loading_speed*0.3f) // Slowing down
- .load_move_x(oldx, 10, 60*std::hypot(dist,10.f)/10.f * loading_speed*0.1f) // Super slow
+ .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
+ .load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
+ .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down
+ .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow
+ .travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate
.resume_preview();
// Reset the extruder current to the normal value.
@@ -1057,7 +1055,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
}
// Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
-void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool,bool brim)
+void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume)
{
assert(m_plan.back().z <= z_par + WT_EPSILON ); // refuses to add a layer below the last one
@@ -1082,13 +1080,13 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
float ramming_depth = depth;
length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
float first_wipe_line = -length_to_extrude;
- length_to_extrude += volume_to_length(wipe_volumes[old_tool][new_tool], m_perimeter_width, layer_height_par);
+ length_to_extrude += volume_to_length(wipe_volume, m_perimeter_width, layer_height_par);
length_to_extrude = std::max(length_to_extrude,0.f);
depth += (int(length_to_extrude / width) + 1) * m_perimeter_width;
depth *= m_extra_spacing;
- m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth,first_wipe_line));
+ m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth, first_wipe_line, wipe_volume));
}
@@ -1128,7 +1126,7 @@ void WipeTowerPrusaMM::save_on_last_wipe()
float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
float length_to_save = 2*(m_wipe_tower_width+m_wipe_tower_depth) + (!layer_finished() ? finish_layer().total_extrusion_length_in_plane() : 0.f);
- float length_to_wipe = volume_to_length(wipe_volumes[m_layer_info->tool_changes.back().old_tool][m_layer_info->tool_changes.back().new_tool],
+ float length_to_wipe = volume_to_length(m_layer_info->tool_changes.back().wipe_volume,
m_perimeter_width,m_layer_info->height) - m_layer_info->tool_changes.back().first_wipe_line - length_to_save;
length_to_wipe = std::max(length_to_wipe,0.f);
@@ -1145,7 +1143,8 @@ void WipeTowerPrusaMM::save_on_last_wipe()
void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
{
if (m_plan.empty())
- return;
+
+ return;
m_extra_spacing = 1.f;
@@ -1165,8 +1164,6 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
for (auto layer : m_plan)
{
set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
-
-
if (m_peters_wipe_tower)
m_wipe_tower_rotation_angle += 90.f;
else
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index b7c721128..1ae4616d8 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -5,6 +5,7 @@
#include <string>
#include <sstream>
#include <utility>
+#include <algorithm>
#include "WipeTower.hpp"
@@ -43,8 +44,8 @@ public:
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
// wipe_area -- space available for one toolchange in mm
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
- float cooling_tube_length, float parking_pos_retraction, float bridging, const std::vector<float>& wiping_matrix,
- unsigned int initial_tool) :
+ float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
+ const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_tower_rotation_angle(rotation_angle),
@@ -54,20 +55,19 @@ public:
m_cooling_tube_retraction(cooling_tube_retraction),
m_cooling_tube_length(cooling_tube_length),
m_parking_pos_retraction(parking_pos_retraction),
+ m_extra_loading_move(extra_loading_move),
m_bridging(bridging),
- m_current_tool(initial_tool)
- {
- unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+WT_EPSILON);
- for (unsigned int i = 0; i<number_of_extruders; ++i)
- wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders,wiping_matrix.begin()+(i+1)*number_of_extruders));
- }
+ m_current_tool(initial_tool),
+ wipe_volumes(wiping_matrix)
+ {}
virtual ~WipeTowerPrusaMM() {}
// Set the extruder properties.
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
- float unloading_speed, float delay, std::string ramming_parameters, float nozzle_diameter)
+ float unloading_speed, float delay, int cooling_moves, float cooling_initial_speed,
+ float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
{
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
m_filpar.push_back(FilamentParameters());
@@ -78,7 +78,9 @@ public:
m_filpar[idx].loading_speed = loading_speed;
m_filpar[idx].unloading_speed = unloading_speed;
m_filpar[idx].delay = delay;
- m_filpar[idx].cooling_time = 14.f; // let's fix it for now, cooling moves will be reworked for 1.41 anyway
+ m_filpar[idx].cooling_moves = cooling_moves;
+ m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
+ m_filpar[idx].cooling_final_speed = cooling_final_speed;
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
@@ -95,7 +97,7 @@ public:
// Appends into internal structure m_plan containing info about the future wipe tower
// to be used before building begins. The entries must be added ordered in z.
- void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim);
+ void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f);
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
@@ -192,11 +194,13 @@ private:
float m_layer_height = 0.f; // Current layer height.
size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
+ int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
// G-code generator parameters.
float m_cooling_tube_retraction = 0.f;
float m_cooling_tube_length = 0.f;
float m_parking_pos_retraction = 0.f;
+ float m_extra_loading_move = 0.f;
float m_bridging = 0.f;
bool m_adhesion = true;
@@ -211,7 +215,9 @@ private:
float loading_speed = 0.f;
float unloading_speed = 0.f;
float delay = 0.f ;
- float cooling_time = 0.f;
+ int cooling_moves = 0;
+ float cooling_initial_speed = 0.f;
+ float cooling_final_speed = 0.f;
float ramming_line_width_multiplicator = 0.f;
float ramming_step_multiplicator = 0.f;
std::vector<float> ramming_speed;
@@ -229,14 +235,13 @@ private:
bool m_print_brim = true;
// A fill-in direction (positive Y, negative Y) alternates with each layer.
wipe_shape m_current_shape = SHAPE_NORMAL;
- unsigned int m_current_tool;
- std::vector<std::vector<float>> wipe_volumes;
+ unsigned int m_current_tool = 0;
+ const std::vector<std::vector<float>> wipe_volumes;
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
bool m_left_to_right = true;
float m_extra_spacing = 1.f;
-
// Calculates extrusion flow needed to produce required line width for given layer height
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
{
@@ -247,7 +252,7 @@ private:
// Calculates length of extrusion line to extrude given volume
float volume_to_length(float volume, float line_width, float layer_height) const {
- return volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.)));
+ return std::max(0., volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.))));
}
// Calculates depth for all layers and propagates them downwards
@@ -300,8 +305,9 @@ private:
float required_depth;
float ramming_depth;
float first_wipe_line;
- ToolChange(unsigned int old,unsigned int newtool,float depth=0.f,float ramming_depth=0.f,float fwl=0.f)
- : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth},first_wipe_line{fwl} {}
+ float wipe_volume;
+ ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
+ : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
};
float z; // z position of the layer
float height; // layer height
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index fc49d68af..73234d3eb 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -21,6 +21,7 @@
// #include <benchmark.h>
#include "SVG.hpp"
+#include <Eigen/Dense>
namespace Slic3r {
@@ -990,10 +991,7 @@ void ModelObject::clear_instances()
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
-//========================================================================================================
const BoundingBoxf3& ModelObject::bounding_box() const
-//const BoundingBoxf3& ModelObject::bounding_box()
-//========================================================================================================
{
if (! m_bounding_box_valid) {
BoundingBoxf3 raw_bbox;
@@ -1435,32 +1433,16 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const
{
- // rotate around mesh origin
- double c = cos(this->rotation);
- double s = sin(this->rotation);
- Pointf3 pts[4] = {
- bbox.min,
- bbox.max,
- Pointf3(bbox.min.x, bbox.max.y, bbox.min.z),
- Pointf3(bbox.max.x, bbox.min.y, bbox.max.z)
- };
- BoundingBoxf3 out;
- for (int i = 0; i < 4; ++ i) {
- Pointf3 &v = pts[i];
- double xold = v.x;
- double yold = v.y;
- v.x = float(c * xold - s * yold);
- v.y = float(s * xold + c * yold);
- v.x *= this->scaling_factor;
- v.y *= this->scaling_factor;
- v.z *= this->scaling_factor;
- if (! dont_translate) {
- v.x += this->offset.x;
- v.y += this->offset.y;
- }
- out.merge(v);
- }
- return out;
+ Eigen::Transform<float, 3, Eigen::Affine> matrix = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
+ if (!dont_translate)
+ matrix.translate(Eigen::Vector3f((float)offset.x, (float)offset.y, 0.0f));
+
+ matrix.rotate(Eigen::AngleAxisf(rotation, Eigen::Vector3f::UnitZ()));
+ matrix.scale(scaling_factor);
+
+ std::vector<float> m(16, 0.0f);
+ ::memcpy((void*)m.data(), (const void*)matrix.data(), 16 * sizeof(float));
+ return bbox.transformed(m);
}
void ModelInstance::transform_polygon(Polygon* polygon) const
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index 3337f4d9a..5003f8330 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -103,10 +103,7 @@ public:
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
// This bounding box is being cached.
-//========================================================================================================
const BoundingBoxf3& bounding_box() const;
-// const BoundingBoxf3& bounding_box();
-//========================================================================================================
void invalidate_bounding_box() { m_bounding_box_valid = false; }
// Returns a snug bounding box of the transformed instances.
// This bounding box is not being cached.
@@ -148,10 +145,9 @@ private:
// Parent object, owning this ModelObject.
Model *m_model;
// Bounding box, cached.
-//========================================================================================================
+
mutable BoundingBoxf3 m_bounding_box;
mutable bool m_bounding_box_valid;
-//========================================================================================================
};
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 3dcc67863..d10d1a9dc 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -165,6 +165,11 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
std::vector<PrintStep> steps;
std::vector<PrintObjectStep> osteps;
bool invalidated = false;
+
+ // Always invalidate the wipe tower. This is probably necessary because of the wipe_into_infill / wipe_into_objects
+ // features - nearly anything can influence what should (and could) be wiped into.
+ steps.emplace_back(psWipeTower);
+
for (const t_config_option_key &opt_key : opt_keys) {
if (steps_ignore.find(opt_key) != steps_ignore.end()) {
// These options only affect G-code export or they are just notes without influence on the generated G-code,
@@ -191,6 +196,9 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "filament_loading_speed"
|| opt_key == "filament_unloading_speed"
|| opt_key == "filament_toolchange_delay"
+ || opt_key == "filament_cooling_moves"
+ || opt_key == "filament_cooling_initial_speed"
+ || opt_key == "filament_cooling_final_speed"
|| opt_key == "filament_ramming_parameters"
|| opt_key == "gcode_flavor"
|| opt_key == "single_extruder_multi_material"
@@ -206,6 +214,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "parking_pos_retraction"
|| opt_key == "cooling_tube_retraction"
|| opt_key == "cooling_tube_length"
+ || opt_key == "extra_loading_move"
|| opt_key == "z_offset") {
steps.emplace_back(psWipeTower);
} else if (
@@ -217,7 +226,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
osteps.emplace_back(posSupportMaterial);
steps.emplace_back(psSkirt);
steps.emplace_back(psBrim);
- steps.emplace_back(psWipeTower);
} else {
// for legacy, if we can't handle this option let's invalidate all steps
//FIXME invalidate all steps of all objects as well?
@@ -1028,6 +1036,14 @@ void Print::_make_wipe_tower()
if (! this->has_wipe_tower())
return;
+ // Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
+ std::vector<float> wiping_matrix((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end());
+ // Extract purging volumes for each extruder pair:
+ std::vector<std::vector<float>> wipe_volumes;
+ const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON);
+ for (unsigned int i = 0; i<number_of_extruders; ++i)
+ wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
+
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
m_tool_ordering = ToolOrdering(*this, (unsigned int)-1, true);
if (! m_tool_ordering.has_wipe_tower())
@@ -1043,7 +1059,7 @@ void Print::_make_wipe_tower()
size_t idx_end = m_tool_ordering.layer_tools().size();
// Find the first wipe tower layer, which does not have a counterpart in an object or a support layer.
for (size_t i = 0; i < idx_end; ++ i) {
- const ToolOrdering::LayerTools &lt = m_tool_ordering.layer_tools()[i];
+ const LayerTools &lt = m_tool_ordering.layer_tools()[i];
if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) {
idx_begin = i;
break;
@@ -1057,7 +1073,7 @@ void Print::_make_wipe_tower()
for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer);
// Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer.
for (size_t i = idx_begin; i < idx_end; ++ i) {
- ToolOrdering::LayerTools &lt = const_cast<ToolOrdering::LayerTools&>(m_tool_ordering.layer_tools()[i]);
+ LayerTools &lt = const_cast<LayerTools&>(m_tool_ordering.layer_tools()[i]);
if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support))
break;
lt.has_support = true;
@@ -1072,22 +1088,20 @@ void Print::_make_wipe_tower()
}
}
- // Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
- std::vector<float> wiping_volumes((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end());
-
// Initialize the wipe tower.
WipeTowerPrusaMM wipe_tower(
float(this->config.wipe_tower_x.value), float(this->config.wipe_tower_y.value),
float(this->config.wipe_tower_width.value),
float(this->config.wipe_tower_rotation_angle.value), float(this->config.cooling_tube_retraction.value),
float(this->config.cooling_tube_length.value), float(this->config.parking_pos_retraction.value),
- float(this->config.wipe_tower_bridging), wiping_volumes, m_tool_ordering.first_extruder());
+ float(this->config.extra_loading_move.value), float(this->config.wipe_tower_bridging), wipe_volumes,
+ m_tool_ordering.first_extruder());
//wipe_tower.set_retract();
//wipe_tower.set_zhop();
// Set the extruder & material properties at the wipe tower object.
- for (size_t i = 0; i < (int)(sqrt(wiping_volumes.size())+EPSILON); ++ i)
+ for (size_t i = 0; i < number_of_extruders; ++ i)
wipe_tower.set_extruder(
i,
WipeTowerPrusaMM::parse_material(this->config.filament_type.get_at(i).c_str()),
@@ -1096,91 +1110,44 @@ void Print::_make_wipe_tower()
this->config.filament_loading_speed.get_at(i),
this->config.filament_unloading_speed.get_at(i),
this->config.filament_toolchange_delay.get_at(i),
+ this->config.filament_cooling_moves.get_at(i),
+ this->config.filament_cooling_initial_speed.get_at(i),
+ this->config.filament_cooling_final_speed.get_at(i),
this->config.filament_ramming_parameters.get_at(i),
this->config.nozzle_diameter.get_at(i));
- // When printing the first layer's wipe tower, the first extruder is expected to be active and primed.
- // Therefore the number of wipe sections at the wipe tower will be (m_tool_ordering.front().extruders-1) at the 1st layer.
- // The following variable is true if the last priming section cannot be squeezed inside the wipe tower.
- bool last_priming_wipe_full = m_tool_ordering.front().extruders.size() > m_tool_ordering.front().wipe_tower_partitions;
-
m_wipe_tower_priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
- wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), ! last_priming_wipe_full));
-
+ wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), false));
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
{
unsigned int current_extruder_id = m_tool_ordering.all_extruders().back();
- for (const auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers
+ for (auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers
if (!layer_tools.has_wipe_tower) continue;
bool first_layer = &layer_tools == &m_tool_ordering.front();
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
for (const auto extruder_id : layer_tools.extruders) {
if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
- wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back());
+ float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
+
+ // try to assign some infills/objects for the wiping:
+ volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
+
+ wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
current_extruder_id = extruder_id;
}
}
+ layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
if (&layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
break;
}
}
-
-
// Generate the wipe tower layers.
m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size());
wipe_tower.generate(m_wipe_tower_tool_changes);
- // Set current_extruder_id to the last extruder primed.
- /*unsigned int current_extruder_id = m_tool_ordering.all_extruders().back();
-
- for (const ToolOrdering::LayerTools &layer_tools : m_tool_ordering.layer_tools()) {
- if (! layer_tools.has_wipe_tower)
- // This is a support only layer, or the wipe tower does not reach to this height.
- continue;
- bool first_layer = &layer_tools == &m_tool_ordering.front();
- bool last_layer = &layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0;
- wipe_tower.set_layer(
- float(layer_tools.print_z),
- float(layer_tools.wipe_tower_layer_height),
- layer_tools.wipe_tower_partitions,
- first_layer,
- last_layer);
- std::vector<WipeTower::ToolChangeResult> tool_changes;
- for (unsigned int extruder_id : layer_tools.extruders)
- // Call the wipe_tower.tool_change() at the first layer for the initial extruder
- // to extrude the wipe tower brim,
- if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) ||
- // or when an extruder shall be switched.
- extruder_id != current_extruder_id) {
- tool_changes.emplace_back(wipe_tower.tool_change(extruder_id, extruder_id == layer_tools.extruders.back(), WipeTower::PURPOSE_EXTRUDE));
- current_extruder_id = extruder_id;
- }
- if (! wipe_tower.layer_finished()) {
- tool_changes.emplace_back(wipe_tower.finish_layer(WipeTower::PURPOSE_EXTRUDE));
- if (tool_changes.size() > 1) {
- // Merge the two last tool changes into one.
- WipeTower::ToolChangeResult &tc1 = tool_changes[tool_changes.size() - 2];
- WipeTower::ToolChangeResult &tc2 = tool_changes.back();
- if (tc1.end_pos != tc2.start_pos) {
- // Add a travel move from tc1.end_pos to tc2.start_pos.
- char buf[2048];
- sprintf(buf, "G1 X%.3f Y%.3f F7200\n", tc2.start_pos.x, tc2.start_pos.y);
- tc1.gcode += buf;
- }
- tc1.gcode += tc2.gcode;
- append(tc1.extrusions, tc2.extrusions);
- tc1.end_pos = tc2.end_pos;
- tool_changes.pop_back();
- }
- }
- m_wipe_tower_tool_changes.emplace_back(std::move(tool_changes));
- if (last_layer)
- break;
- }*/
-
// Unload the current filament over the purge tower.
coordf_t layer_height = this->objects.front()->config.layer_height.value;
if (m_tool_ordering.back().wipe_tower_partitions > 0) {
@@ -1201,6 +1168,10 @@ void Print::_make_wipe_tower()
wipe_tower.tool_change((unsigned int)-1, false));
}
+
+
+
+
std::string Print::output_filename()
{
this->placeholder_parser.update_timestamp();
@@ -1239,4 +1210,13 @@ void Print::set_status(int percent, const std::string &message)
printf("Print::status %d => %s\n", percent, message.c_str());
}
+
+// Returns extruder this eec should be printed with, according to PrintRegion config
+int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region)
+{
+ return is_infill(fill.role()) ? std::max<int>(0, (is_solid_infill(fill.entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
+ std::max<int>(region.config.perimeter_extruder.value - 1, 0);
+}
+
+
}
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index eec5c2478..2217547ea 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -24,6 +24,7 @@ class Print;
class PrintObject;
class ModelObject;
+
// Print step IDs for keeping track of the print state.
enum PrintStep {
psSkirt, psBrim, psWipeTower, psCount,
@@ -286,6 +287,9 @@ public:
bool has_support_material() const;
void auto_assign_extruders(ModelObject* model_object) const;
+ // Returns extruder this eec should be printed with, according to PrintRegion config:
+ static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region);
+
void _make_skirt();
void _make_brim();
@@ -312,7 +316,8 @@ public:
void restart() { m_canceled = false; }
// Has the calculation been canceled?
bool canceled() { return m_canceled; }
-
+
+
private:
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
@@ -321,6 +326,7 @@ private:
tbb::atomic<bool> m_canceled;
};
+
#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
#define FOREACH_REGION(print, region) FOREACH_BASE(PrintRegionPtrs, (print)->regions, region)
#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->objects, object)
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index a6b5654ec..f541089a3 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -352,6 +352,7 @@ PrintConfigDef::PrintConfigDef()
def->enum_labels.push_back("2");
def->enum_labels.push_back("3");
def->enum_labels.push_back("4");
+ def->enum_labels.push_back("5");
def = this->add("extruder_clearance_height", coFloat);
def->label = L("Height");
@@ -491,6 +492,31 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloats { 0. };
+ def = this->add("filament_cooling_moves", coInts);
+ def->label = L("Number of cooling moves");
+ def->tooltip = L("Filament is cooled by being moved back and forth in the "
+ "cooling tubes. Specify desired number of these moves ");
+ def->cli = "filament-cooling-moves=i@";
+ def->max = 0;
+ def->max = 20;
+ def->default_value = new ConfigOptionInts { 4 };
+
+ def = this->add("filament_cooling_initial_speed", coFloats);
+ def->label = L("Speed of the first cooling move");
+ def->tooltip = L("Cooling moves are gradually accelerating beginning at this speed. ");
+ def->cli = "filament-cooling-initial-speed=i@";
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 2.2f };
+
+ def = this->add("filament_cooling_final_speed", coFloats);
+ def->label = L("Speed of the last cooling move");
+ def->tooltip = L("Cooling moves are gradually accelerating towards this speed. ");
+ def->cli = "filament-cooling-final-speed=i@";
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 3.4f };
+
def = this->add("filament_ramming_parameters", coStrings);
def->label = L("Ramming parameters");
def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters ");
@@ -1142,6 +1168,15 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloat(92.f);
+ def = this->add("extra_loading_move", coFloat);
+ def->label = L("Extra loading distance");
+ def->tooltip = L("When set to zero, the distance the filament is moved from parking position during load "
+ "is exactly the same as it was moved back during unload. When positive, it is loaded further, "
+ " if negative, the loading move is shorter than unloading. ");
+ def->sidetext = L("mm");
+ def->cli = "extra_loading_move=f";
+ def->default_value = new ConfigOptionFloat(-2.f);
+
def = this->add("perimeter_acceleration", coFloat);
def->label = L("Perimeters");
def->tooltip = L("This is the acceleration your printer will use for perimeters. "
@@ -1955,7 +1990,25 @@ PrintConfigDef::PrintConfigDef()
def->sidetext = L("degrees");
def->cli = "wipe-tower-rotation-angle=f";
def->default_value = new ConfigOptionFloat(0.);
-
+
+ def = this->add("wipe_into_infill", coBool);
+ def->category = L("Extruders");
+ def->label = L("Purging into infill");
+ def->tooltip = L("Wiping after toolchange will be preferentially done inside infills. "
+ "This lowers the amount of waste but may result in longer print time "
+ " due to additional travel moves.");
+ def->cli = "wipe-into-infill!";
+ def->default_value = new ConfigOptionBool(false);
+
+ def = this->add("wipe_into_objects", coBool);
+ def->category = L("Extruders");
+ def->label = L("Purging into objects");
+ def->tooltip = L("Objects will be used to wipe the nozzle after a toolchange to save material "
+ "that would otherwise end up in the wipe tower and decrease print time. "
+ "Colours of the objects will be mixed as a result.");
+ def->cli = "wipe-into-objects!";
+ def->default_value = new ConfigOptionBool(false);
+
def = this->add("wipe_tower_bridging", coFloat);
def->label = L("Maximal bridging distance");
def->tooltip = L("Maximal distance between supports on sparse infill sections. ");
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index aad27222e..c530868a1 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -336,7 +336,8 @@ public:
ConfigOptionBool support_material_with_sheath;
ConfigOptionFloatOrPercent support_material_xy_spacing;
ConfigOptionFloat xy_size_compensation;
-
+ ConfigOptionBool wipe_into_objects;
+
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
@@ -372,6 +373,7 @@ protected:
OPT_PTR(support_material_threshold);
OPT_PTR(support_material_with_sheath);
OPT_PTR(xy_size_compensation);
+ OPT_PTR(wipe_into_objects);
}
};
@@ -414,7 +416,8 @@ public:
ConfigOptionFloatOrPercent top_infill_extrusion_width;
ConfigOptionInt top_solid_layers;
ConfigOptionFloatOrPercent top_solid_infill_speed;
-
+ ConfigOptionBool wipe_into_infill;
+
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
@@ -452,6 +455,7 @@ protected:
OPT_PTR(top_infill_extrusion_width);
OPT_PTR(top_solid_infill_speed);
OPT_PTR(top_solid_layers);
+ OPT_PTR(wipe_into_infill);
}
};
@@ -526,6 +530,9 @@ public:
ConfigOptionFloats filament_loading_speed;
ConfigOptionFloats filament_unloading_speed;
ConfigOptionFloats filament_toolchange_delay;
+ ConfigOptionInts filament_cooling_moves;
+ ConfigOptionFloats filament_cooling_initial_speed;
+ ConfigOptionFloats filament_cooling_final_speed;
ConfigOptionStrings filament_ramming_parameters;
ConfigOptionBool gcode_comments;
ConfigOptionEnum<GCodeFlavor> gcode_flavor;
@@ -556,7 +563,7 @@ public:
ConfigOptionFloat cooling_tube_length;
ConfigOptionFloat parking_pos_retraction;
ConfigOptionBool silent_mode;
-
+ ConfigOptionFloat extra_loading_move;
std::string get_extrusion_axis() const
{
@@ -584,6 +591,9 @@ protected:
OPT_PTR(filament_loading_speed);
OPT_PTR(filament_unloading_speed);
OPT_PTR(filament_toolchange_delay);
+ OPT_PTR(filament_cooling_moves);
+ OPT_PTR(filament_cooling_initial_speed);
+ OPT_PTR(filament_cooling_final_speed);
OPT_PTR(filament_ramming_parameters);
OPT_PTR(gcode_comments);
OPT_PTR(gcode_flavor);
@@ -614,6 +624,7 @@ protected:
OPT_PTR(cooling_tube_length);
OPT_PTR(parking_pos_retraction);
OPT_PTR(silent_mode);
+ OPT_PTR(extra_loading_move);
}
};
diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index ba0876a85..7ac165864 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -93,6 +93,7 @@ bool PrintObject::set_copies(const Points &points)
bool invalidated = this->_print->invalidate_step(psSkirt);
invalidated |= this->_print->invalidate_step(psBrim);
+ invalidated |= this->_print->invalidate_step(psWipeTower);
return invalidated;
}
@@ -232,7 +233,10 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|| opt_key == "perimeter_speed"
|| opt_key == "small_perimeter_speed"
|| opt_key == "solid_infill_speed"
- || opt_key == "top_solid_infill_speed") {
+ || opt_key == "top_solid_infill_speed"
+ || opt_key == "wipe_into_infill" // when these these two are changed, we only need to invalidate the wipe tower,
+ || opt_key == "wipe_into_objects" // which we already did at the very beginning - nothing more to be done
+ ) {
// these options only affect G-code export, so nothing to invalidate
} else {
// for legacy, if we can't handle this option let's invalidate all steps
@@ -272,6 +276,8 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
}
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
+ // It also decides about what the wipe_into_infill / wipe_into_object features will do,
+ // and that too depends on many of the settings.
invalidated |= this->_print->invalidate_step(psWipeTower);
return invalidated;
}
diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp
index 921841a27..a501fa4d3 100644
--- a/xs/src/libslic3r/Utils.hpp
+++ b/xs/src/libslic3r/Utils.hpp
@@ -96,7 +96,8 @@ public:
void call(int i, int j) const;
void call(const std::vector<int>& ints) const;
void call(double d) const;
- void call(double x, double y) const;
+ void call(double a, double b) const;
+ void call(double a, double b, double c, double d) const;
void call(bool b) const;
private:
void *m_callback;
diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp
index 991118c14..13ec1d066 100644
--- a/xs/src/libslic3r/utils.cpp
+++ b/xs/src/libslic3r/utils.cpp
@@ -266,7 +266,7 @@ void PerlCallback::call(double d) const
LEAVE;
}
-void PerlCallback::call(double x, double y) const
+void PerlCallback::call(double a, double b) const
{
if (!m_callback)
return;
@@ -274,8 +274,26 @@ void PerlCallback::call(double x, double y) const
ENTER;
SAVETMPS;
PUSHMARK(SP);
- XPUSHs(sv_2mortal(newSVnv(x)));
- XPUSHs(sv_2mortal(newSVnv(y)));
+ XPUSHs(sv_2mortal(newSVnv(a)));
+ XPUSHs(sv_2mortal(newSVnv(b)));
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(double a, double b, double c, double d) const
+{
+ if (!m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVnv(a)));
+ XPUSHs(sv_2mortal(newSVnv(b)));
+ XPUSHs(sv_2mortal(newSVnv(c)));
+ XPUSHs(sv_2mortal(newSVnv(d)));
PUTBACK;
perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
FREETMPS;
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index f1d36b4a9..d8fe592e8 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -2,7 +2,6 @@
#include "3DScene.hpp"
-#include "../../libslic3r/libslic3r.h"
#include "../../libslic3r/ExtrusionEntity.hpp"
#include "../../libslic3r/ExtrusionEntityCollection.hpp"
#include "../../libslic3r/Geometry.hpp"
@@ -28,8 +27,15 @@
#include <wx/image.h>
#include <wx/settings.h>
+#include <Eigen/Dense>
+
#include "GUI.hpp"
+static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
+
namespace Slic3r {
void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh)
@@ -198,6 +204,34 @@ const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f };
const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f };
const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f };
+GLVolume::GLVolume(float r, float g, float b, float a)
+ : m_angle_z(0.0f)
+ , m_scale_factor(1.0f)
+ , m_dirty(true)
+ , composite_id(-1)
+ , select_group_id(-1)
+ , drag_group_id(-1)
+ , extruder_id(0)
+ , selected(false)
+ , is_active(true)
+ , zoom_to_volumes(true)
+ , outside_printer_detection_enabled(true)
+ , is_outside(false)
+ , hover(false)
+ , is_modifier(false)
+ , is_wipe_tower(false)
+ , tverts_range(0, size_t(-1))
+ , qverts_range(0, size_t(-1))
+{
+ m_world_mat = std::vector<float>(UNIT_MATRIX, std::end(UNIT_MATRIX));
+
+ color[0] = r;
+ color[1] = g;
+ color[2] = b;
+ color[3] = a;
+ set_render_color(r, g, b, a);
+}
+
void GLVolume::set_render_color(float r, float g, float b, float a)
{
render_color[0] = r;
@@ -218,12 +252,7 @@ void GLVolume::set_render_color(const float* rgba, unsigned int size)
void GLVolume::set_render_color()
{
if (selected)
- {
- if (is_outside)
- set_render_color(SELECTED_OUTSIDE_COLOR, 4);
- else
- set_render_color(SELECTED_COLOR, 4);
- }
+ set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
else if (hover)
set_render_color(HOVER_COLOR, 4);
else if (is_outside)
@@ -232,6 +261,52 @@ void GLVolume::set_render_color()
set_render_color(color, 4);
}
+const Pointf3& GLVolume::get_origin() const
+{
+ return m_origin;
+}
+
+void GLVolume::set_origin(const Pointf3& origin)
+{
+ m_origin = origin;
+ m_dirty = true;
+}
+
+void GLVolume::set_angle_z(float angle_z)
+{
+ m_angle_z = angle_z;
+ m_dirty = true;
+}
+
+void GLVolume::set_scale_factor(float scale_factor)
+{
+ m_scale_factor = scale_factor;
+ m_dirty = true;
+}
+
+const std::vector<float>& GLVolume::world_matrix() const
+{
+ if (m_dirty)
+ {
+ Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
+ m.translate(Eigen::Vector3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z));
+ m.rotate(Eigen::AngleAxisf(m_angle_z, Eigen::Vector3f::UnitZ()));
+ m.scale(m_scale_factor);
+ ::memcpy((void*)m_world_mat.data(), (const void*)m.data(), 16 * sizeof(float));
+ m_dirty = false;
+ }
+
+ return m_world_mat;
+}
+
+BoundingBoxf3 GLVolume::transformed_bounding_box() const
+{
+ if (m_dirty)
+ m_transformed_bounding_box = bounding_box.transformed(world_matrix());
+
+ return m_transformed_bounding_box;
+}
+
void GLVolume::set_range(double min_z, double max_z)
{
this->qverts_range.first = 0;
@@ -272,14 +347,16 @@ void GLVolume::render() const
if (!is_active)
return;
- glCullFace(GL_BACK);
- glPushMatrix();
- glTranslated(this->origin.x, this->origin.y, this->origin.z);
+ ::glCullFace(GL_BACK);
+ ::glPushMatrix();
+ ::glTranslated(m_origin.x, m_origin.y, m_origin.z);
+ ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
+ ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
if (this->indexed_vertex_array.indexed())
this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
else
this->indexed_vertex_array.render();
- glPopMatrix();
+ ::glPopMatrix();
}
void GLVolume::render_using_layer_height() const
@@ -297,6 +374,7 @@ void GLVolume::render_using_layer_height() const
GLint z_texture_row_to_normalized_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_texture_row_to_normalized") : -1;
GLint z_cursor_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor") : -1;
GLint z_cursor_band_width_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor_band_width") : -1;
+ GLint world_matrix_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "volume_world_matrix") : -1;
if (z_to_texture_row_id >= 0)
glUniform1f(z_to_texture_row_id, (GLfloat)layer_height_texture_z_to_row_id());
@@ -310,14 +388,20 @@ void GLVolume::render_using_layer_height() const
if (z_cursor_band_width_id >= 0)
glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width);
- unsigned int w = layer_height_texture_width();
- unsigned int h = layer_height_texture_height();
+ if (world_matrix_id >= 0)
+ ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
+ GLsizei w = (GLsizei)layer_height_texture_width();
+ GLsizei h = (GLsizei)layer_height_texture_height();
+ GLsizei half_w = w / 2;
+ GLsizei half_h = h / 2;
+
+ ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, layer_height_texture_data.texture_id);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, w / 2, h / 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level0());
- glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, w / 2, h / 2, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1());
+ glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1());
render();
@@ -327,6 +411,128 @@ void GLVolume::render_using_layer_height() const
glUseProgram(current_program_id);
}
+void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const
+{
+ if (!is_active)
+ return;
+
+ if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
+ return;
+
+ if (layer_height_texture_data.can_use())
+ {
+ ::glDisableClientState(GL_VERTEX_ARRAY);
+ ::glDisableClientState(GL_NORMAL_ARRAY);
+ render_using_layer_height();
+ ::glEnableClientState(GL_VERTEX_ARRAY);
+ ::glEnableClientState(GL_NORMAL_ARRAY);
+ return;
+ }
+
+ GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
+ GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
+ if (n_triangles + n_quads == 0)
+ {
+ ::glDisableClientState(GL_VERTEX_ARRAY);
+ ::glDisableClientState(GL_NORMAL_ARRAY);
+
+ if (color_id >= 0)
+ {
+ float color[4];
+ ::memcpy((void*)color, (const void*)render_color, 4 * sizeof(float));
+ ::glUniform4fv(color_id, 1, (const GLfloat*)color);
+ }
+ else
+ ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+
+ if (detection_id != -1)
+ ::glUniform1i(detection_id, outside_printer_detection_enabled ? 1 : 0);
+
+ if (worldmatrix_id != -1)
+ ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
+
+ render();
+
+ ::glEnableClientState(GL_VERTEX_ARRAY);
+ ::glEnableClientState(GL_NORMAL_ARRAY);
+
+ return;
+ }
+
+ if (color_id >= 0)
+ ::glUniform4fv(color_id, 1, (const GLfloat*)render_color);
+ else
+ ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+
+ if (detection_id != -1)
+ ::glUniform1i(detection_id, outside_printer_detection_enabled ? 1 : 0);
+
+ if (worldmatrix_id != -1)
+ ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
+
+ ::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
+ ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)));
+ ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr);
+
+ ::glPushMatrix();
+ ::glTranslated(m_origin.x, m_origin.y, m_origin.z);
+ ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
+ ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
+
+ if (n_triangles > 0)
+ {
+ ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.triangle_indices_VBO_id);
+ ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4));
+ }
+ if (n_quads > 0)
+ {
+ ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.quad_indices_VBO_id);
+ ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4));
+ }
+
+ ::glPopMatrix();
+}
+
+void GLVolume::render_legacy() const
+{
+ assert(!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
+ if (!is_active)
+ return;
+
+ GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
+ GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
+ if (n_triangles + n_quads == 0)
+ {
+ ::glDisableClientState(GL_VERTEX_ARRAY);
+ ::glDisableClientState(GL_NORMAL_ARRAY);
+
+ ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+ render();
+
+ ::glEnableClientState(GL_VERTEX_ARRAY);
+ ::glEnableClientState(GL_NORMAL_ARRAY);
+
+ return;
+ }
+
+ ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
+ ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data() + 3);
+ ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data());
+
+ ::glPushMatrix();
+ ::glTranslated(m_origin.x, m_origin.y, m_origin.z);
+ ::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
+ ::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
+
+ if (n_triangles > 0)
+ ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first);
+
+ if (n_quads > 0)
+ ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, indexed_vertex_array.quad_indices.data() + qverts_range.first);
+
+ ::glPopMatrix();
+}
+
double GLVolume::layer_height_texture_z_to_row_id() const
{
return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max.z);
@@ -399,7 +605,6 @@ std::vector<int> GLVolumeCollection::load_object(
for (int instance_idx : instance_idxs) {
const ModelInstance *instance = model_object->instances[instance_idx];
TriangleMesh mesh = model_volume->mesh;
- instance->transform_mesh(&mesh);
volumes_idx.push_back(int(this->volumes.size()));
float color[4];
memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
@@ -434,13 +639,15 @@ std::vector<int> GLVolumeCollection::load_object(
}
v.is_modifier = model_volume->modifier;
v.outside_printer_detection_enabled = !model_volume->modifier;
+ v.set_origin(Pointf3(instance->offset.x, instance->offset.y, 0.0));
+ v.set_angle_z(instance->rotation);
+ v.set_scale_factor(instance->scaling_factor);
}
}
return volumes_idx;
}
-
int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs)
{
@@ -461,7 +668,8 @@ int GLVolumeCollection::load_wipe_tower_preview(
else
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
- v.origin = Pointf3(pos_x, pos_y, 0.);
+ v.set_origin(Pointf3(pos_x, pos_y, 0.));
+
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box();
v.indexed_vertex_array.finalize_geometry(use_VBOs);
@@ -486,102 +694,23 @@ void GLVolumeCollection::render_VBOs() const
GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1;
GLint print_box_min_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.min") : -1;
GLint print_box_max_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.max") : -1;
- GLint print_box_origin_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_origin") : -1;
+ GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1;
+ GLint print_box_worldmatrix_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1;
- for (GLVolume *volume : this->volumes) {
- if (!volume->is_active)
- continue;
+ if (print_box_min_id != -1)
+ ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
- if (!volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
- continue;
+ if (print_box_max_id != -1)
+ ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
+ for (GLVolume *volume : this->volumes)
+ {
if (volume->layer_height_texture_data.can_use())
- {
- ::glDisableClientState(GL_VERTEX_ARRAY);
- ::glDisableClientState(GL_NORMAL_ARRAY);
volume->generate_layer_height_texture(volume->layer_height_texture_data.print_object, false);
- volume->render_using_layer_height();
- ::glEnableClientState(GL_VERTEX_ARRAY);
- ::glEnableClientState(GL_NORMAL_ARRAY);
- continue;
- }
-
- volume->set_render_color();
-
- GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first));
- GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first));
- if (n_triangles + n_quads == 0)
- {
- ::glDisableClientState(GL_VERTEX_ARRAY);
- ::glDisableClientState(GL_NORMAL_ARRAY);
-
- if (color_id >= 0)
- {
- float color[4];
- ::memcpy((void*)color, (const void*)volume->render_color, 4 * sizeof(float));
- ::glUniform4fv(color_id, 1, (const GLfloat*)color);
- }
- else
- ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
-
- if (print_box_min_id != -1)
- ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
-
- if (print_box_max_id != -1)
- ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
-
- if (print_box_origin_id != -1)
- {
- float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f };
- ::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin);
- }
-
- volume->render();
-
- ::glEnableClientState(GL_VERTEX_ARRAY);
- ::glEnableClientState(GL_NORMAL_ARRAY);
-
- continue;
- }
-
- if (color_id >= 0)
- ::glUniform4fv(color_id, 1, (const GLfloat*)volume->render_color);
else
- ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
-
- if (print_box_min_id != -1)
- ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
-
- if (print_box_max_id != -1)
- ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
-
- if (print_box_origin_id != -1)
- {
- float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f };
- ::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin);
- }
+ volume->set_render_color();
- ::glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
- ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)));
- ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr);
-
- bool has_offset = (volume->origin.x != 0) || (volume->origin.y != 0) || (volume->origin.z != 0);
- if (has_offset) {
- ::glPushMatrix();
- ::glTranslated(volume->origin.x, volume->origin.y, volume->origin.z);
- }
-
- if (n_triangles > 0) {
- ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id);
- ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(volume->tverts_range.first * 4));
- }
- if (n_quads > 0) {
- ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id);
- ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(volume->qverts_range.first * 4));
- }
-
- if (has_offset)
- ::glPopMatrix();
+ volume->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
}
::glBindBuffer(GL_ARRAY_BUFFER, 0);
@@ -602,43 +731,10 @@ void GLVolumeCollection::render_legacy() const
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
- for (GLVolume *volume : this->volumes) {
- assert(! volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
- if (!volume->is_active)
- continue;
-
+ for (GLVolume *volume : this->volumes)
+ {
volume->set_render_color();
-
- GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first));
- GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first));
- if (n_triangles + n_quads == 0)
- {
- ::glDisableClientState(GL_VERTEX_ARRAY);
- ::glDisableClientState(GL_NORMAL_ARRAY);
-
- ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
- volume->render();
-
- ::glEnableClientState(GL_VERTEX_ARRAY);
- ::glEnableClientState(GL_NORMAL_ARRAY);
-
- continue;
- }
-
- glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
- glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data() + 3);
- glNormalPointer(GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data());
- bool has_offset = volume->origin.x != 0 || volume->origin.y != 0 || volume->origin.z != 0;
- if (has_offset) {
- glPushMatrix();
- glTranslated(volume->origin.x, volume->origin.y, volume->origin.z);
- }
- if (n_triangles > 0)
- glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, volume->indexed_vertex_array.triangle_indices.data() + volume->tverts_range.first);
- if (n_quads > 0)
- glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, volume->indexed_vertex_array.quad_indices.data() + volume->qverts_range.first);
- if (has_offset)
- glPopMatrix();
+ volume->render_legacy();
}
glDisableClientState(GL_VERTEX_ARRAY);
@@ -1486,11 +1582,12 @@ GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
unsigned int _3DScene::TextureBase::finalize()
{
- if (!m_data.empty()) {
+ if ((m_tex_id == 0) && !m_data.empty()) {
// sends buffer to gpu
+ ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glGenTextures(1, &m_tex_id);
- ::glBindTexture(GL_TEXTURE_2D, m_tex_id);
- ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)m_data.data());
+ ::glBindTexture(GL_TEXTURE_2D, (GLuint)m_tex_id);
+ ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)m_data.data());
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
@@ -1949,6 +2046,11 @@ void _3DScene::update_volumes_colors_by_extruder(wxGLCanvas* canvas)
s_canvas_mgr.update_volumes_colors_by_extruder(canvas);
}
+void _3DScene::update_gizmos_data(wxGLCanvas* canvas)
+{
+ s_canvas_mgr.update_gizmos_data(canvas);
+}
+
void _3DScene::render(wxGLCanvas* canvas)
{
s_canvas_mgr.render(canvas);
@@ -2044,6 +2146,16 @@ void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, vo
s_canvas_mgr.register_on_gizmo_scale_uniformly_callback(canvas, callback);
}
+void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback);
+}
+
+void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback)
+{
+ s_canvas_mgr.register_on_update_geometry_info_callback(canvas, callback);
+}
+
static inline int hex_digit_to_int(const char c)
{
return
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index 9016f984d..f7fc75db5 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -240,7 +240,7 @@ class GLVolume {
edit_band_width = 0.0f;
}
- bool can_use() { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); }
+ bool can_use() const { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); }
};
public:
@@ -249,44 +249,27 @@ public:
static const float OUTSIDE_COLOR[4];
static const float SELECTED_OUTSIDE_COLOR[4];
- GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) :
- composite_id(-1),
- select_group_id(-1),
- drag_group_id(-1),
- extruder_id(0),
- selected(false),
- is_active(true),
- zoom_to_volumes(true),
- outside_printer_detection_enabled(true),
- is_outside(false),
- hover(false),
- is_modifier(false),
- is_wipe_tower(false),
- tverts_range(0, size_t(-1)),
- qverts_range(0, size_t(-1))
- {
- color[0] = r;
- color[1] = g;
- color[2] = b;
- color[3] = a;
- set_render_color(r, g, b, a);
- }
+ GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
- std::vector<int> load_object(
- const ModelObject *model_object,
- const std::vector<int> &instance_idxs,
- const std::string &color_by,
- const std::string &select_by,
- const std::string &drag_by);
+private:
+ // Offset of the volume to be rendered.
+ Pointf3 m_origin;
+ // Rotation around Z axis of the volume to be rendered.
+ float m_angle_z;
+ // Scale factor of the volume to be rendered.
+ float m_scale_factor;
+ // World matrix of the volume to be rendered.
+ std::vector<float> m_world_mat;
+ // Bounding box of this volume, in unscaled coordinates.
+ mutable BoundingBoxf3 m_transformed_bounding_box;
+ // Whether or not is needed to recalculate the world matrix.
+ mutable bool m_dirty;
- int load_wipe_tower_preview(
- int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
+public:
// Bounding box of this volume, in unscaled coordinates.
BoundingBoxf3 bounding_box;
- // Offset of the volume to be rendered.
- Pointf3 origin;
// Color of the triangles / quads held by this volume.
float color[4];
// Color used to render this volume.
@@ -333,10 +316,17 @@ public:
// Sets render color in dependence of current state
void set_render_color();
+ const Pointf3& get_origin() const;
+ void set_origin(const Pointf3& origin);
+ void set_angle_z(float angle_z);
+ void set_scale_factor(float scale_factor);
+
int object_idx() const { return this->composite_id / 1000000; }
int volume_idx() const { return (this->composite_id / 1000) % 1000; }
int instance_idx() const { return this->composite_id % 1000; }
- BoundingBoxf3 transformed_bounding_box() const { BoundingBoxf3 bb = this->bounding_box; bb.translate(this->origin); return bb; }
+
+ const std::vector<float>& world_matrix() const;
+ BoundingBoxf3 transformed_bounding_box() const;
bool empty() const { return this->indexed_vertex_array.empty(); }
bool indexed() const { return this->indexed_vertex_array.indexed(); }
@@ -344,6 +334,9 @@ public:
void set_range(coordf_t low, coordf_t high);
void render() const;
void render_using_layer_height() const;
+ void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const;
+ void render_legacy() const;
+
void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
@@ -568,6 +561,7 @@ public:
static void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other);
static void update_volumes_colors_by_extruder(wxGLCanvas* canvas);
+ static void update_gizmos_data(wxGLCanvas* canvas);
static void render(wxGLCanvas* canvas);
@@ -590,6 +584,8 @@ public:
static void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback);
static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
+ static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
static std::vector<int> load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs);
static std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
index adea27fa4..208125a15 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1,5 +1,6 @@
#include "GLCanvas3D.hpp"
+#include "../../libslic3r/libslic3r.h"
#include "../../slic3r/GUI/3DScene.hpp"
#include "../../slic3r/GUI/GLShader.hpp"
#include "../../slic3r/GUI/GUI.hpp"
@@ -41,6 +42,11 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f };
static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f;
static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f;
+static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f };
+
namespace Slic3r {
namespace GUI {
@@ -493,6 +499,7 @@ void GLCanvas3D::Bed::_render_prusa(float theta) const
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
::glEnable(GL_TEXTURE_2D);
+ ::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -500,7 +507,6 @@ void GLCanvas3D::Bed::_render_prusa(float theta) const
if (theta > 90.0f)
::glFrontFace(GL_CW);
- ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id());
::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices());
::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords());
@@ -553,6 +559,7 @@ void GLCanvas3D::Bed::_render_custom() const
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisable(GL_BLEND);
+ ::glDisable(GL_LIGHTING);
}
}
@@ -577,7 +584,6 @@ GLCanvas3D::Axes::Axes()
void GLCanvas3D::Axes::render(bool depth_test) const
{
- ::glDisable(GL_LIGHTING);
if (depth_test)
::glEnable(GL_DEPTH_TEST);
else
@@ -623,7 +629,6 @@ bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons)
void GLCanvas3D::CuttingPlane::render(const BoundingBoxf3& bb) const
{
- ::glDisable(GL_LIGHTING);
_render_plane(bb);
_render_contour();
}
@@ -730,6 +735,12 @@ void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const
m_shader->set_uniform(name.c_str(), value);
}
+void GLCanvas3D::Shader::set_uniform(const std::string& name, const float* matrix) const
+{
+ if (m_shader != nullptr)
+ m_shader->set_uniform(name.c_str(), matrix);
+}
+
const GLShader* GLCanvas3D::Shader::get_shader() const
{
return m_shader;
@@ -963,15 +974,18 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)volume.layer_height_texture_height());
m_shader.set_uniform("z_cursor", max_z * get_cursor_z_relative(canvas));
m_shader.set_uniform("z_cursor_band_width", band_width);
+ // The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix
+ m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX);
GLsizei w = (GLsizei)volume.layer_height_texture_width();
GLsizei h = (GLsizei)volume.layer_height_texture_height();
GLsizei half_w = w / 2;
GLsizei half_h = h / 2;
+ ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glBindTexture(GL_TEXTURE_2D, m_z_texture_id);
- ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
- ::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ ::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level0());
::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level1());
@@ -1053,7 +1067,9 @@ const Pointf3 GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MA
GLCanvas3D::Mouse::Drag::Drag()
: start_position_2D(Invalid_2D_Point)
, start_position_3D(Invalid_3D_Point)
- , volume_idx(-1)
+ , move_with_shift(false)
+ , move_volume_idx(-1)
+ , gizmo_volume_idx(-1)
{
}
@@ -1083,8 +1099,9 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const
return (drag.start_position_3D != Drag::Invalid_3D_Point);
}
-const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f;
-const float GLCanvas3D::Gizmos::OverlayGapY = 10.0f;
+const float GLCanvas3D::Gizmos::OverlayTexturesScale = 0.75f;
+const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale;
+const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale;
GLCanvas3D::Gizmos::Gizmos()
: m_enabled(false)
@@ -1150,7 +1167,7 @@ void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Poin
if (it->second == nullptr)
continue;
- float tex_size = (float)it->second->get_textures_size();
+ float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
float half_tex_size = 0.5f * tex_size;
// we currently use circular icons for gizmo, so we check the radius
@@ -1176,7 +1193,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Poi
if (it->second == nullptr)
continue;
- float tex_size = (float)it->second->get_textures_size();
+ float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
float half_tex_size = 0.5f * tex_size;
// we currently use circular icons for gizmo, so we check the radius
@@ -1242,7 +1259,7 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const
if (it->second == nullptr)
continue;
- float tex_size = (float)it->second->get_textures_size();
+ float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
float half_tex_size = 0.5f * tex_size;
// we currently use circular icons for gizmo, so we check the radius
@@ -1274,14 +1291,19 @@ void GLCanvas3D::Gizmos::update(const Pointf& mouse_pos)
curr->update(mouse_pos);
}
-void GLCanvas3D::Gizmos::update_data(float scale)
+void GLCanvas3D::Gizmos::refresh()
{
if (!m_enabled)
return;
- GizmosMap::const_iterator it = m_gizmos.find(Scale);
- if (it != m_gizmos.end())
- reinterpret_cast<GLGizmoScale*>(it->second)->set_scale(scale);
+ GLGizmoBase* curr = _get_current();
+ if (curr != nullptr)
+ curr->refresh();
+}
+
+GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const
+{
+ return m_current;
}
bool GLCanvas3D::Gizmos::is_running() const
@@ -1309,6 +1331,9 @@ void GLCanvas3D::Gizmos::start_dragging()
void GLCanvas3D::Gizmos::stop_dragging()
{
m_dragging = false;
+ GLGizmoBase* curr = _get_current();
+ if (curr != nullptr)
+ curr->stop_dragging();
}
float GLCanvas3D::Gizmos::get_scale() const
@@ -1320,6 +1345,35 @@ float GLCanvas3D::Gizmos::get_scale() const
return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoScale*>(it->second)->get_scale() : 1.0f;
}
+void GLCanvas3D::Gizmos::set_scale(float scale)
+{
+ if (!m_enabled)
+ return;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Scale);
+ if (it != m_gizmos.end())
+ reinterpret_cast<GLGizmoScale*>(it->second)->set_scale(scale);
+}
+
+float GLCanvas3D::Gizmos::get_angle_z() const
+{
+ if (!m_enabled)
+ return 0.0f;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Rotate);
+ return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoRotate*>(it->second)->get_angle_z() : 0.0f;
+}
+
+void GLCanvas3D::Gizmos::set_angle_z(float angle_z)
+{
+ if (!m_enabled)
+ return;
+
+ GizmosMap::const_iterator it = m_gizmos.find(Rotate);
+ if (it != m_gizmos.end())
+ reinterpret_cast<GLGizmoRotate*>(it->second)->set_angle_z(angle_z);
+}
+
void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const
{
if (!m_enabled)
@@ -1375,8 +1429,8 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const
float scaled_gap_y = OverlayGapY * inv_zoom;
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
- float tex_size = (float)it->second->get_textures_size() * inv_zoom;
- GLTexture::render_texture(it->second->get_textures_id(), top_x, top_x + tex_size, top_y - tex_size, top_y);
+ float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale * inv_zoom;
+ GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y);
top_y -= (tex_size + scaled_gap_y);
}
}
@@ -1849,6 +1903,42 @@ void GLCanvas3D::update_volumes_colors_by_extruder()
m_volumes.update_colors_by_extruder(m_config);
}
+void GLCanvas3D::update_gizmos_data()
+{
+ if (!m_gizmos.is_running())
+ return;
+
+ int id = _get_first_selected_object_id();
+ if ((id != -1) && (m_model != nullptr))
+ {
+ ModelObject* model_object = m_model->objects[id];
+ if (model_object != nullptr)
+ {
+ ModelInstance* model_instance = model_object->instances[0];
+ if (model_instance != nullptr)
+ {
+ switch (m_gizmos.get_current_type())
+ {
+ case Gizmos::Scale:
+ {
+ m_gizmos.set_scale(model_instance->scaling_factor);
+ break;
+ }
+ case Gizmos::Rotate:
+ {
+ m_gizmos.set_angle_z(model_instance->rotation);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
void GLCanvas3D::render()
{
if (m_canvas == nullptr)
@@ -1961,6 +2051,7 @@ void GLCanvas3D::reload_scene(bool force)
m_objects_volumes_idxs.push_back(load_object(*m_model, obj_idx));
}
+ update_gizmos_data();
update_volumes_selection(m_objects_selections);
if (m_config->has("nozzle_diameter"))
@@ -2512,6 +2603,18 @@ void GLCanvas3D::register_on_gizmo_scale_uniformly_callback(void* callback)
m_on_gizmo_scale_uniformly_callback.register_callback(callback);
}
+void GLCanvas3D::register_on_gizmo_rotate_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_gizmo_rotate_callback.register_callback(callback);
+}
+
+void GLCanvas3D::register_on_update_geometry_info_callback(void* callback)
+{
+ if (callback != nullptr)
+ m_on_update_geometry_info_callback.register_callback(callback);
+}
+
void GLCanvas3D::bind_event_handlers()
{
if (m_canvas != nullptr)
@@ -2729,14 +2832,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
else if ((selected_object_idx != -1) && gizmos_overlay_contains_mouse)
{
+ update_gizmos_data();
m_gizmos.update_on_off_state(*this, m_mouse.position);
- _update_gizmos_data();
m_dirty = true;
}
else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse())
- {
- _update_gizmos_data();
+ {
+ update_gizmos_data();
m_gizmos.start_dragging();
+ m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id();
m_dirty = true;
}
else
@@ -2761,9 +2865,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
- if (m_gizmos.is_running())
- _update_gizmos_data();
-
+ update_gizmos_data();
+ m_gizmos.refresh();
m_dirty = true;
}
}
@@ -2786,7 +2889,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (volume_bbox.contains(pos3d))
{
// The dragging operation is initiated.
- m_mouse.drag.volume_idx = volume_idx;
+ m_mouse.drag.move_with_shift = evt.ShiftDown();
+ m_mouse.drag.move_volume_idx = volume_idx;
m_mouse.drag.start_position_3D = pos3d;
// Remember the shift to to the object center.The object center will later be used
// to limit the object placement close to the bed.
@@ -2802,7 +2906,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
}
- else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.volume_idx != -1))
+ else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1))
{
m_mouse.dragging = true;
@@ -2825,27 +2929,34 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// Calculate the translation vector.
Vectorf3 vector = m_mouse.drag.start_position_3D.vector_to(cur_pos);
// Get the volume being dragged.
- GLVolume* volume = m_volumes.volumes[m_mouse.drag.volume_idx];
+ GLVolume* volume = m_volumes.volumes[m_mouse.drag.move_volume_idx];
// Get all volumes belonging to the same group, if any.
std::vector<GLVolume*> volumes;
- if (volume->drag_group_id == -1)
+ int group_id = m_mouse.drag.move_with_shift ? volume->select_group_id : volume->drag_group_id;
+ if (group_id == -1)
volumes.push_back(volume);
else
{
for (GLVolume* v : m_volumes.volumes)
{
- if ((v != nullptr) && (v->drag_group_id == volume->drag_group_id))
- volumes.push_back(v);
+ if (v != nullptr)
+ {
+ if ((m_mouse.drag.move_with_shift && (v->select_group_id == group_id)) || (v->drag_group_id == group_id))
+ volumes.push_back(v);
+ }
}
}
// Apply new temporary volume origin and ignore Z.
for (GLVolume* v : volumes)
{
- v->origin.translate(vector.x, vector.y, 0.0);
+ Pointf3 origin = v->get_origin();
+ origin.translate(vector.x, vector.y, 0.0);
+ v->set_origin(origin);
}
m_mouse.drag.start_position_3D = cur_pos;
+ m_gizmos.refresh();
m_dirty = true;
}
@@ -2856,7 +2967,59 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
const Pointf3& cur_pos = _mouse_to_bed_3d(pos);
m_gizmos.update(Pointf(cur_pos.x, cur_pos.y));
- m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
+ std::vector<GLVolume*> volumes;
+ if (m_mouse.drag.gizmo_volume_idx != -1)
+ {
+ GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx];
+ // Get all volumes belonging to the same group, if any.
+ if (volume->select_group_id == -1)
+ volumes.push_back(volume);
+ else
+ {
+ for (GLVolume* v : m_volumes.volumes)
+ {
+ if ((v != nullptr) && (v->select_group_id == volume->select_group_id))
+ volumes.push_back(v);
+ }
+ }
+ }
+
+ switch (m_gizmos.get_current_type())
+ {
+ case Gizmos::Scale:
+ {
+ // Apply new temporary scale factor
+ float scale_factor = m_gizmos.get_scale();
+ for (GLVolume* v : volumes)
+ {
+ v->set_scale_factor(scale_factor);
+ }
+ break;
+ }
+ case Gizmos::Rotate:
+ {
+ // Apply new temporary angle_z
+ float angle_z = m_gizmos.get_angle_z();
+ for (GLVolume* v : volumes)
+ {
+ v->set_angle_z(angle_z);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (!volumes.empty())
+ {
+ const BoundingBoxf3& bb = volumes[0]->transformed_bounding_box();
+ const Pointf3& size = bb.size();
+ m_on_update_geometry_info_callback.call(size.x, size.y, size.z, m_gizmos.get_scale());
+ }
+
+ if ((m_gizmos.get_current_type() != Gizmos::Rotate) && (volumes.size() > 1))
+ m_gizmos.refresh();
+
m_dirty = true;
}
else if (evt.Dragging() && !gizmos_overlay_contains_mouse)
@@ -2914,19 +3077,19 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (layer_editing_object_idx != -1)
m_on_model_update_callback.call();
}
- else if ((m_mouse.drag.volume_idx != -1) && m_mouse.dragging)
+ else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging)
{
// get all volumes belonging to the same group, if any
std::vector<int> volume_idxs;
- int vol_id = m_mouse.drag.volume_idx;
- int group_id = m_volumes.volumes[vol_id]->drag_group_id;
+ int vol_id = m_mouse.drag.move_volume_idx;
+ int group_id = m_mouse.drag.move_with_shift ? m_volumes.volumes[vol_id]->select_group_id : m_volumes.volumes[vol_id]->drag_group_id;
if (group_id == -1)
volume_idxs.push_back(vol_id);
else
{
for (int i = 0; i < (int)m_volumes.volumes.size(); ++i)
{
- if (m_volumes.volumes[i]->drag_group_id == group_id)
+ if ((m_mouse.drag.move_with_shift && (m_volumes.volumes[i]->select_group_id == group_id)) || (m_volumes.volumes[i]->drag_group_id == group_id))
volume_idxs.push_back(i);
}
}
@@ -2944,10 +3107,26 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
else if (evt.LeftUp() && m_gizmos.is_dragging())
{
+ switch (m_gizmos.get_current_type())
+ {
+ case Gizmos::Scale:
+ {
+ m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
+ break;
+ }
+ case Gizmos::Rotate:
+ {
+ m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z());
+ break;
+ }
+ default:
+ break;
+ }
m_gizmos.stop_dragging();
}
- m_mouse.drag.volume_idx = -1;
+ m_mouse.drag.move_volume_idx = -1;
+ m_mouse.drag.gizmo_volume_idx = -1;
m_mouse.set_start_position_3D_as_invalid();
m_mouse.set_start_position_2D_as_invalid();
m_mouse.dragging = false;
@@ -3200,6 +3379,8 @@ void GLCanvas3D::_deregister_callbacks()
m_on_wipe_tower_moved_callback.deregister_callback();
m_on_enable_action_buttons_callback.deregister_callback();
m_on_gizmo_scale_uniformly_callback.deregister_callback();
+ m_on_gizmo_rotate_callback.deregister_callback();
+ m_on_update_geometry_info_callback.deregister_callback();
}
void GLCanvas3D::_mark_volumes_for_layer_height() const
@@ -3259,7 +3440,6 @@ void GLCanvas3D::_picking_pass() const
if (m_multisample_allowed)
::glDisable(GL_MULTISAMPLE);
- ::glDisable(GL_LIGHTING);
::glDisable(GL_BLEND);
::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@@ -3315,8 +3495,6 @@ void GLCanvas3D::_render_background() const
static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f };
- ::glDisable(GL_LIGHTING);
-
::glPushMatrix();
::glLoadIdentity();
::glMatrixMode(GL_PROJECTION);
@@ -3395,6 +3573,8 @@ void GLCanvas3D::_render_objects() const
if (m_picking_enabled)
::glEnable(GL_CULL_FACE);
}
+
+ ::glDisable(GL_LIGHTING);
}
void GLCanvas3D::_render_cutting_plane() const
@@ -3459,6 +3639,7 @@ void GLCanvas3D::_render_legend_texture() const
float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom;
float r = l + (float)w * inv_zoom;
float b = t - (float)h * inv_zoom;
+
GLTexture::render_texture(tex_id, l, r, b, t);
::glPopMatrix();
@@ -3503,9 +3684,7 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
{
static const GLfloat INV_255 = 1.0f / 255.0f;
- if (fake_colors)
- ::glDisable(GL_LIGHTING);
- else
+ if (!fake_colors)
::glEnable(GL_LIGHTING);
// do not cull backfaces to show broken geometry, if any
@@ -3543,6 +3722,9 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
::glDisable(GL_BLEND);
::glEnable(GL_CULL_FACE);
+
+ if (!fake_colors)
+ ::glDisable(GL_LIGHTING);
}
void GLCanvas3D::_render_gizmo() const
@@ -3668,6 +3850,35 @@ int GLCanvas3D::_get_first_selected_object_id() const
return -1;
}
+int GLCanvas3D::_get_first_selected_volume_id() const
+{
+ if (m_print != nullptr)
+ {
+ int objects_count = (int)m_print->objects.size();
+
+ for (const GLVolume* vol : m_volumes.volumes)
+ {
+ if ((vol != nullptr) && vol->selected)
+ {
+ int object_id = vol->select_group_id / 1000000;
+ // Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy.
+ if ((object_id < 10000) && (object_id < objects_count))
+ {
+ int volume_id = 0;
+ for (int i = 0; i < object_id; ++i)
+ {
+ const PrintObject* obj = m_print->objects[i];
+ const ModelObject* model = obj->model_object();
+ volume_id += model->instances.size();
+ }
+ return volume_id;
+ }
+ }
+ }
+ }
+ return -1;
+}
+
static inline int hex_digit_to_int(const char c)
{
return
@@ -4273,13 +4484,14 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
{
// Move a regular object.
ModelObject* model_object = m_model->objects[obj_idx];
- model_object->instances[instance_idx]->offset.translate(volume->origin.x, volume->origin.y);
+ const Pointf3& origin = volume->get_origin();
+ model_object->instances[instance_idx]->offset = Pointf(origin.x, origin.y);
model_object->invalidate_bounding_box();
object_moved = true;
}
else if (obj_idx == 1000)
// Move a wipe tower proxy.
- wipe_tower_origin = volume->origin;
+ wipe_tower_origin = volume->get_origin();
}
if (object_moved)
@@ -4302,21 +4514,6 @@ void GLCanvas3D::_on_select(int volume_idx)
m_on_select_object_callback.call(id);
}
-void GLCanvas3D::_update_gizmos_data()
-{
- int id = _get_first_selected_object_id();
- if ((id != -1) && (m_model != nullptr))
- {
- ModelObject* model_object = m_model->objects[id];
- if (model_object != nullptr)
- {
- ModelInstance* model_instance = model_object->instances[0];
- if (model_instance != nullptr)
- m_gizmos.update_data(model_instance->scaling_factor);
- }
- }
-}
-
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
{
static const float INV_255 = 1.0f / 255.0f;
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp
index b0706a05d..cc998226b 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp
@@ -225,6 +225,7 @@ public:
void stop_using() const;
void set_uniform(const std::string& name, float value) const;
+ void set_uniform(const std::string& name, const float* matrix) const;
const GLShader* get_shader() const;
@@ -302,7 +303,10 @@ public:
Point start_position_2D;
Pointf3 start_position_3D;
Vectorf3 volume_center_offset;
- int volume_idx;
+
+ bool move_with_shift;
+ int move_volume_idx;
+ int gizmo_volume_idx;
public:
Drag();
@@ -323,6 +327,7 @@ public:
class Gizmos
{
+ static const float OverlayTexturesScale;
static const float OverlayOffsetX;
static const float OverlayGapY;
@@ -360,14 +365,21 @@ public:
bool overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const;
bool grabber_contains_mouse() const;
void update(const Pointf& mouse_pos);
- void update_data(float scale);
+ void refresh();
+
+ EType get_current_type() const;
bool is_running() const;
+
bool is_dragging() const;
void start_dragging();
void stop_dragging();
float get_scale() const;
+ void set_scale(float scale);
+
+ float get_angle_z() const;
+ void set_angle_z(float angle_z);
void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const;
void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const;
@@ -439,6 +451,8 @@ private:
PerlCallback m_on_wipe_tower_moved_callback;
PerlCallback m_on_enable_action_buttons_callback;
PerlCallback m_on_gizmo_scale_uniformly_callback;
+ PerlCallback m_on_gizmo_rotate_callback;
+ PerlCallback m_on_update_geometry_info_callback;
public:
GLCanvas3D(wxGLCanvas* canvas);
@@ -507,6 +521,7 @@ public:
void set_viewport_from_scene(const GLCanvas3D& other);
void update_volumes_colors_by_extruder();
+ void update_gizmos_data();
void render();
@@ -545,6 +560,8 @@ public:
void register_on_wipe_tower_moved_callback(void* callback);
void register_on_enable_action_buttons_callback(void* callback);
void register_on_gizmo_scale_uniformly_callback(void* callback);
+ void register_on_gizmo_rotate_callback(void* callback);
+ void register_on_update_geometry_info_callback(void* callback);
void bind_event_handlers();
void unbind_event_handlers();
@@ -605,6 +622,7 @@ private:
void _stop_timer();
int _get_first_selected_object_id() const;
+ int _get_first_selected_volume_id() const;
// generates gcode extrusion paths geometry
void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
@@ -625,8 +643,6 @@ private:
void _on_move(const std::vector<int>& volume_idxs);
void _on_select(int volume_idx);
- void _update_gizmos_data();
-
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
};
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
index ec4ac1606..7a68cbc81 100644
--- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
@@ -464,6 +464,13 @@ void GLCanvas3DManager::update_volumes_colors_by_extruder(wxGLCanvas* canvas)
it->second->update_volumes_colors_by_extruder();
}
+void GLCanvas3DManager::update_gizmos_data(wxGLCanvas* canvas)
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->update_gizmos_data();
+}
+
void GLCanvas3DManager::render(wxGLCanvas* canvas) const
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
@@ -655,6 +662,20 @@ void GLCanvas3DManager::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* c
it->second->register_on_gizmo_scale_uniformly_callback(callback);
}
+void GLCanvas3DManager::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_gizmo_rotate_callback(callback);
+}
+
+void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback)
+{
+ CanvasesMap::iterator it = _get_canvas(canvas);
+ if (it != m_canvases.end())
+ it->second->register_on_update_geometry_info_callback(callback);
+}
+
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas)
{
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
index 9d9285601..c813fd477 100644
--- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
@@ -120,6 +120,7 @@ public:
void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other);
void update_volumes_colors_by_extruder(wxGLCanvas* canvas);
+ void update_gizmos_data(wxGLCanvas* canvas);
void render(wxGLCanvas* canvas) const;
@@ -152,6 +153,8 @@ public:
void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback);
void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
+ void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
private:
CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas);
diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
index d3aae33e8..47b01e8a2 100644
--- a/xs/src/slic3r/GUI/GLGizmo.cpp
+++ b/xs/src/slic3r/GUI/GLGizmo.cpp
@@ -90,9 +90,10 @@ GLGizmoBase::EState GLGizmoBase::get_state() const
void GLGizmoBase::set_state(GLGizmoBase::EState state)
{
m_state = state;
+ on_set_state();
}
-unsigned int GLGizmoBase::get_textures_id() const
+unsigned int GLGizmoBase::get_texture_id() const
{
return m_textures[m_state].get_id();
}
@@ -118,12 +119,22 @@ void GLGizmoBase::start_dragging()
on_start_dragging();
}
+void GLGizmoBase::stop_dragging()
+{
+ on_stop_dragging();
+}
+
void GLGizmoBase::update(const Pointf& mouse_pos)
{
if (m_hover_id != -1)
on_update(mouse_pos);
}
+void GLGizmoBase::refresh()
+{
+ on_refresh();
+}
+
void GLGizmoBase::render(const BoundingBoxf3& box) const
{
on_render(box);
@@ -134,13 +145,29 @@ void GLGizmoBase::render_for_picking(const BoundingBoxf3& box) const
on_render_for_picking(box);
}
+void GLGizmoBase::on_set_state()
+{
+ // do nothing
+}
+
void GLGizmoBase::on_start_dragging()
{
+ // do nothing
+}
+
+void GLGizmoBase::on_stop_dragging()
+{
+ // do nothing
+}
+
+void GLGizmoBase::on_refresh()
+{
+ // do nothing
}
void GLGizmoBase::render_grabbers() const
{
- for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
+ for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].render(m_hover_id == i);
}
@@ -162,9 +189,23 @@ GLGizmoRotate::GLGizmoRotate()
, m_angle_z(0.0f)
, m_center(Pointf(0.0, 0.0))
, m_radius(0.0f)
+ , m_keep_radius(false)
{
}
+float GLGizmoRotate::get_angle_z() const
+{
+ return m_angle_z;
+}
+
+void GLGizmoRotate::set_angle_z(float angle_z)
+{
+ if (std::abs(angle_z - 2.0f * PI) < EPSILON)
+ angle_z = 0.0f;
+
+ m_angle_z = angle_z;
+}
+
bool GLGizmoRotate::on_init()
{
std::string path = resources_dir() + "/icons/overlay/";
@@ -186,6 +227,11 @@ bool GLGizmoRotate::on_init()
return true;
}
+void GLGizmoRotate::on_set_state()
+{
+ m_keep_radius = (m_state == On) ? false : true;
+}
+
void GLGizmoRotate::on_update(const Pointf& mouse_pos)
{
Vectorf orig_dir(1.0, 0.0);
@@ -194,6 +240,7 @@ void GLGizmoRotate::on_update(const Pointf& mouse_pos)
if (cross(orig_dir, new_dir) < 0.0)
theta = 2.0 * (coordf_t)PI - theta;
+ // snap
if (length(m_center.vector_to(mouse_pos)) < 2.0 * (double)m_radius / 3.0)
{
coordf_t step = 2.0 * (coordf_t)PI / (coordf_t)SnapRegionsCount;
@@ -202,18 +249,26 @@ void GLGizmoRotate::on_update(const Pointf& mouse_pos)
if (theta == 2.0 * (coordf_t)PI)
theta = 0.0;
-
+
m_angle_z = (float)theta;
}
+void GLGizmoRotate::on_refresh()
+{
+ m_keep_radius = false;
+}
+
void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
{
- ::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
const Pointf3& size = box.size();
m_center = box.center();
- m_radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y));
+ if (!m_keep_radius)
+ {
+ m_radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y));
+ m_keep_radius = true;
+ }
::glLineWidth(2.0f);
::glColor3fv(BaseColor);
@@ -230,7 +285,6 @@ void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const
{
- ::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
m_grabbers[0].color[0] = 1.0f;
@@ -399,7 +453,6 @@ void GLGizmoScale::on_update(const Pointf& mouse_pos)
void GLGizmoScale::on_render(const BoundingBoxf3& box) const
{
- ::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
coordf_t min_x = box.min.x - (coordf_t)Offset;
@@ -438,7 +491,6 @@ void GLGizmoScale::on_render_for_picking(const BoundingBoxf3& box) const
{
static const GLfloat INV_255 = 1.0f / 255.0f;
- ::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
for (unsigned int i = 0; i < 4; ++i)
diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp
index 2baec8f9b..506b3972e 100644
--- a/xs/src/slic3r/GUI/GLGizmo.hpp
+++ b/xs/src/slic3r/GUI/GLGizmo.hpp
@@ -57,22 +57,27 @@ public:
EState get_state() const;
void set_state(EState state);
- unsigned int get_textures_id() const;
+ unsigned int get_texture_id() const;
int get_textures_size() const;
int get_hover_id() const;
void set_hover_id(int id);
void start_dragging();
+ void stop_dragging();
void update(const Pointf& mouse_pos);
+ void refresh();
void render(const BoundingBoxf3& box) const;
void render_for_picking(const BoundingBoxf3& box) const;
protected:
virtual bool on_init() = 0;
+ virtual void on_set_state();
virtual void on_start_dragging();
+ virtual void on_stop_dragging();
virtual void on_update(const Pointf& mouse_pos) = 0;
+ virtual void on_refresh();
virtual void on_render(const BoundingBoxf3& box) const = 0;
virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0;
@@ -96,13 +101,19 @@ class GLGizmoRotate : public GLGizmoBase
mutable Pointf m_center;
mutable float m_radius;
+ mutable bool m_keep_radius;
public:
GLGizmoRotate();
+ float get_angle_z() const;
+ void set_angle_z(float angle_z);
+
protected:
virtual bool on_init();
+ virtual void on_set_state();
virtual void on_update(const Pointf& mouse_pos);
+ virtual void on_refresh();
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
@@ -120,9 +131,9 @@ class GLGizmoScale : public GLGizmoBase
static const float Offset;
float m_scale;
+ float m_starting_scale;
Pointf m_starting_drag_position;
- float m_starting_scale;
public:
GLGizmoScale();
diff --git a/xs/src/slic3r/GUI/GLShader.cpp b/xs/src/slic3r/GUI/GLShader.cpp
index 903f6c347..e2995f7c3 100644
--- a/xs/src/slic3r/GUI/GLShader.cpp
+++ b/xs/src/slic3r/GUI/GLShader.cpp
@@ -214,6 +214,17 @@ bool GLShader::set_uniform(const char *name, float value) const
return false;
}
+bool GLShader::set_uniform(const char* name, const float* matrix) const
+{
+ int id = get_uniform_location(name);
+ if (id >= 0)
+ {
+ ::glUniformMatrix4fv(id, 1, GL_FALSE, (const GLfloat*)matrix);
+ return true;
+ }
+ return false;
+}
+
/*
# Set shader vector
sub SetVector
diff --git a/xs/src/slic3r/GUI/GLShader.hpp b/xs/src/slic3r/GUI/GLShader.hpp
index 032640d8d..803b2f154 100644
--- a/xs/src/slic3r/GUI/GLShader.hpp
+++ b/xs/src/slic3r/GUI/GLShader.hpp
@@ -25,6 +25,7 @@ public:
int get_uniform_location(const char *name) const;
bool set_uniform(const char *name, float value) const;
+ bool set_uniform(const char* name, const float* matrix) const;
void enable() const;
void disable() const;
diff --git a/xs/src/slic3r/GUI/GLTexture.cpp b/xs/src/slic3r/GUI/GLTexture.cpp
index 924920bd8..2af555707 100644
--- a/xs/src/slic3r/GUI/GLTexture.cpp
+++ b/xs/src/slic3r/GUI/GLTexture.cpp
@@ -72,9 +72,10 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap
}
// sends data to gpu
+ ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glGenTextures(1, &m_id);
::glBindTexture(GL_TEXTURE_2D, m_id);
- ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
+ ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
if (generate_mipmaps)
{
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
@@ -127,27 +128,25 @@ const std::string& GLTexture::get_source() const
void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top)
{
- ::glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
-
- ::glDisable(GL_LIGHTING);
::glEnable(GL_BLEND);
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
::glEnable(GL_TEXTURE_2D);
+ ::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id);
::glBegin(GL_QUADS);
- ::glTexCoord2d(0.0f, 1.0f); ::glVertex3f(left, bottom, 0.0f);
- ::glTexCoord2d(1.0f, 1.0f); ::glVertex3f(right, bottom, 0.0f);
- ::glTexCoord2d(1.0f, 0.0f); ::glVertex3f(right, top, 0.0f);
- ::glTexCoord2d(0.0f, 0.0f); ::glVertex3f(left, top, 0.0f);
+ ::glTexCoord2f(0.0f, 1.0f); ::glVertex2f(left, bottom);
+ ::glTexCoord2f(1.0f, 1.0f); ::glVertex2f(right, bottom);
+ ::glTexCoord2f(1.0f, 0.0f); ::glVertex2f(right, top);
+ ::glTexCoord2f(0.0f, 0.0f); ::glVertex2f(left, top);
::glEnd();
::glBindTexture(GL_TEXTURE_2D, 0);
::glDisable(GL_TEXTURE_2D);
::glDisable(GL_BLEND);
- ::glEnable(GL_LIGHTING);
}
void GLTexture::_generate_mipmaps(wxImage& image)
@@ -182,7 +181,7 @@ void GLTexture::_generate_mipmaps(wxImage& image)
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
}
- ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
+ ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
}
}
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index c2aae2fe5..af7022f2b 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -342,7 +342,7 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
auto local_menu = new wxMenu();
wxWindowID config_id_base = wxWindow::NewControlId((int)ConfigMenuCnt);
- auto config_wizard_name = _(ConfigWizard::name().wx_str());
+ const auto config_wizard_name = _(ConfigWizard::name().wx_str());
const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), config_wizard_name);
// Cmd+, is standard on OS X - what about other operating systems?
local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip);
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 1bde7cbad..91a2aa7bd 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -298,7 +298,8 @@ const std::vector<std::string>& Preset::print_options()
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects",
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
- "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "compatible_printers", "compatible_printers_condition","inherits"
+ "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "compatible_printers",
+ "compatible_printers_condition","inherits"
};
return s_opts;
}
@@ -308,10 +309,10 @@ const std::vector<std::string>& Preset::filament_options()
static std::vector<std::string> s_opts {
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
"extrusion_multiplier", "filament_density", "filament_cost", "filament_loading_speed", "filament_unloading_speed", "filament_toolchange_delay",
- "filament_ramming_parameters", "temperature", "first_layer_temperature", "bed_temperature",
- "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers",
- "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", "end_filament_gcode","compatible_printers",
- "compatible_printers_condition", "inherits"
+ "filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "temperature",
+ "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed",
+ "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
+ "start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition", "inherits"
};
return s_opts;
}
@@ -325,8 +326,8 @@ const std::vector<std::string>& Preset::printer_options()
"octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
"between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
- "cooling_tube_length", "parking_pos_retraction", "max_print_height", "default_print_profile", "inherits",
- "silent_mode","machine_max_acceleration_extruding", "machine_max_acceleration_retracting",
+ "cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits",
+ "silent_mode", "machine_max_acceleration_extruding", "machine_max_acceleration_retracting",
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
"machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
"machine_min_extruding_rate", "machine_min_travel_rate",
@@ -685,6 +686,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
// Otherwise fill in the list from scratch.
ui->Freeze();
ui->Clear();
+ size_t selected_preset_item = 0;
const Preset &selected_preset = this->get_selected_preset();
// Show wide icons if the currently selected preset is not compatible with the current printer,
@@ -725,7 +727,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
if (i == m_idx_selected)
- ui->SetSelection(ui->GetCount() - 1);
+ selected_preset_item = ui->GetCount() - 1;
}
else
{
@@ -742,10 +744,13 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
ui->Append(it->first, *it->second);
if (it->first == selected)
- ui->SetSelection(ui->GetCount() - 1);
+ selected_preset_item = ui->GetCount() - 1;
}
}
- ui->Thaw();
+
+ ui->SetSelection(selected_preset_item);
+ ui->SetToolTip(ui->GetString(selected_preset_item));
+ ui->Thaw();
}
size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible)
@@ -803,6 +808,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
}
}
ui->SetSelection(selected_preset_item);
+ ui->SetToolTip(ui->GetString(selected_preset_item));
ui->Thaw();
return selected_preset_item;
}
diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp
index a2ee1d2eb..5faee08f1 100644
--- a/xs/src/slic3r/GUI/Preset.hpp
+++ b/xs/src/slic3r/GUI/Preset.hpp
@@ -353,6 +353,10 @@ public:
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
std::string path_from_name(const std::string &new_name) const;
+ // update m_edited_preset.is_external value after loading preset for .ini, .gcode, .amf, .3mf
+ void update_edited_preset_is_external(bool is_external) {
+ m_edited_preset.is_external = is_external; }
+
protected:
// Select a preset, if it exists. If it does not exist, select an invalid (-1) index.
// This is a temporary state, which shall be fixed immediately by the following step.
diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp
index 9892efe1d..adca1b153 100644
--- a/xs/src/slic3r/GUI/PresetBundle.cpp
+++ b/xs/src/slic3r/GUI/PresetBundle.cpp
@@ -565,11 +565,12 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
size_t idx = (i_group == 0) ? 0 : num_extruders + 1;
inherits = inherits_values[idx];
compatible_printers_condition = compatible_printers_condition_values[idx];
- if (is_external)
+ if (is_external) {
presets.load_external_preset(name_or_path, name,
config.opt_string((i_group == 0) ? "print_settings_id" : "printer_settings_id", true),
config);
- else
+ presets.update_edited_preset_is_external(true);
+ } else
presets.load_preset(presets.path_from_name(name), name, config).save();
}
@@ -582,9 +583,10 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
// Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
inherits = inherits_values[1];
compatible_printers_condition = compatible_printers_condition_values[1];
- if (is_external)
+ if (is_external) {
this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config);
- else
+ this->filaments.update_edited_preset_is_external(true);
+ } else
this->filaments.load_preset(this->filaments.path_from_name(name), name, config).save();
this->filament_presets.clear();
this->filament_presets.emplace_back(name);
@@ -613,11 +615,12 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
cfg.opt_string("inherits", true) = inherits_values[i + 1];
// Load all filament presets, but only select the first one in the preset dialog.
Preset *loaded = nullptr;
- if (is_external)
+ if (is_external) {
loaded = &this->filaments.load_external_preset(name_or_path, name,
(i < old_filament_profile_names->values.size()) ? old_filament_profile_names->values[i] : "",
std::move(cfg), i == 0);
- else {
+ this->filaments.update_edited_preset_is_external(true);
+ } else {
// Used by the config wizard when creating a custom setup.
// Therefore this block should only be called for a single extruder.
char suffix[64];
@@ -1181,6 +1184,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
// Fill in the list from scratch.
ui->Freeze();
ui->Clear();
+ size_t selected_preset_item = 0;
const Preset *selected_preset = this->filaments.find_preset(this->filament_presets[idx_extruder]);
// Show wide icons if the currently selected preset is not compatible with the current printer,
// and draw a red flag in front of the selected preset.
@@ -1232,7 +1236,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()),
(bitmap == 0) ? wxNullBitmap : *bitmap);
if (selected)
- ui->SetSelection(ui->GetCount() - 1);
+ selected_preset_item = ui->GetCount() - 1;
}
else
{
@@ -1251,9 +1255,11 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
ui->Append(it->first, *it->second);
if (it->first == selected_str)
- ui->SetSelection(ui->GetCount() - 1);
+ selected_preset_item = ui->GetCount() - 1;
}
}
+ ui->SetSelection(selected_preset_item);
+ ui->SetToolTip(ui->GetString(selected_preset_item));
ui->Thaw();
}
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 9831ac764..911ec0cbe 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -1292,6 +1292,10 @@ void TabFilament::build()
optgroup->append_single_option_line("filament_loading_speed");
optgroup->append_single_option_line("filament_unloading_speed");
optgroup->append_single_option_line("filament_toolchange_delay");
+ optgroup->append_single_option_line("filament_cooling_moves");
+ optgroup->append_single_option_line("filament_cooling_initial_speed");
+ optgroup->append_single_option_line("filament_cooling_final_speed");
+
line = { _(L("Ramming")), "" };
line.widget = [this](wxWindow* parent){
auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
@@ -1806,6 +1810,7 @@ void TabPrinter::build_extruder_pages()
optgroup->append_single_option_line("cooling_tube_retraction");
optgroup->append_single_option_line("cooling_tube_length");
optgroup->append_single_option_line("parking_pos_retraction");
+ optgroup->append_single_option_line("extra_loading_move");
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
m_has_single_extruder_MM_page = true;
}
@@ -1859,7 +1864,6 @@ void TabPrinter::build_extruder_pages()
m_pages.begin() + n_before_extruders + m_extruders_count_old);
m_extruders_count_old = m_extruders_count;
-
rebuild_page_tree();
}
diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp
index 48bd30cd1..65dfc8e8e 100644
--- a/xs/xsp/GUI_3DScene.xsp
+++ b/xs/xsp/GUI_3DScene.xsp
@@ -56,9 +56,13 @@
int volume_idx() const;
int instance_idx() const;
Clone<Pointf3> origin() const
- %code%{ RETVAL = THIS->origin; %};
+ %code%{ RETVAL = THIS->get_origin(); %};
void translate(double x, double y, double z)
- %code%{ THIS->origin.translate(x, y, z); %};
+ %code%{
+ Pointf3 o = THIS->get_origin();
+ o.translate(x, y, z);
+ THIS->set_origin(o);
+ %};
Clone<BoundingBoxf3> bounding_box() const
%code%{ RETVAL = THIS->bounding_box; %};
Clone<BoundingBoxf3> transformed_bounding_box() const;
@@ -470,6 +474,12 @@ update_volumes_colors_by_extruder(canvas)
_3DScene::update_volumes_colors_by_extruder((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
void
+update_gizmos_data(canvas)
+ SV *canvas;
+ CODE:
+ _3DScene::update_gizmos_data((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
+
+void
render(canvas)
SV *canvas;
CODE:
@@ -604,6 +614,20 @@ register_on_gizmo_scale_uniformly_callback(canvas, callback)
CODE:
_3DScene::register_on_gizmo_scale_uniformly_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+void
+register_on_gizmo_rotate_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_gizmo_rotate_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
+void
+register_on_update_geometry_info_callback(canvas, callback)
+ SV *canvas;
+ SV *callback;
+ CODE:
+ _3DScene::register_on_update_geometry_info_callback((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), (void*)callback);
+
unsigned int
finalize_legend_texture()
CODE: