From cb0a66b743c119a0ccb71fd257f0efe51cd89503 Mon Sep 17 00:00:00 2001 From: bubnikv <bubnikv@gmail.com> Date: Wed, 17 May 2017 16:53:40 +0200 Subject: [PATCH] Initial implementation of a wipe tower preview UI. --- lib/Slic3r/GUI/3DScene.pm | 10 +++--- lib/Slic3r/GUI/Plater.pm | 21 ++++++++++--- lib/Slic3r/GUI/Plater/3D.pm | 57 +++++++++++++++++++++++++---------- lib/Slic3r/GUI/Tab.pm | 8 +++-- xs/src/slic3r/GUI/3DScene.cpp | 31 +++++++++++++++---- xs/src/slic3r/GUI/3DScene.hpp | 6 ++++ xs/xsp/GUI_3DScene.xsp | 5 +-- 7 files changed, 102 insertions(+), 36 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index db21b3409..0bef735e3 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -240,11 +240,13 @@ sub layer_editing_allowed { return ! (defined($self->{layer_editing_initialized}) && $self->{layer_editing_initialized} == 2); } -sub _first_selected_object_id { +sub _first_selected_object_id_for_variable_layer_height_editing { my ($self) = @_; for my $i (0..$#{$self->volumes}) { if ($self->volumes->[$i]->selected) { - return int($self->volumes->[$i]->select_group_id / 1000000); + 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 if $object_id < 10000; } } return -1; @@ -332,7 +334,7 @@ sub mouse_event { my ($self, $e) = @_; my $pos = Slic3r::Pointf->new($e->GetPositionXY); - my $object_idx_selected = $self->{layer_height_edit_last_object_id} = ($self->layer_editing_enabled && $self->{print}) ? $self->_first_selected_object_id : -1; + 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; if ($e->Entering && &Wx::wxMSW) { # wxMSW needs focus in order to catch mouse wheel events @@ -502,7 +504,7 @@ sub mouse_wheel_event { my ($self, $e) = @_; if ($self->layer_editing_enabled && $self->{print}) { - my $object_idx_selected = $self->_first_selected_object_id; + 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)) { diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 1446c5023..9c75f99ae 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -53,6 +53,8 @@ sub new { $self->{config} = Slic3r::Config->new_from_defaults(qw( bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height serial_port serial_speed octoprint_host octoprint_apikey + nozzle_diameter single_extruder_multi_material + wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe )); # C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm $self->{model} = Slic3r::Model->new; @@ -77,7 +79,8 @@ sub new { # Initialize handlers for canvases my $on_select_object = sub { my ($obj_idx) = @_; - $self->select_object($obj_idx); + # Ignore the special objects (the wipe tower proxy and such). + $self->select_object((defined($obj_idx) && $obj_idx < 1000) ? $obj_idx : undef); }; my $on_double_click = sub { $self->object_settings_dialog if $self->selected_object; @@ -538,7 +541,8 @@ sub on_layer_editing_toggled { $self->{"btn_layer_editing"}->SetValue(0); } } - $self->{canvas3D}->update; + $self->{canvas3D}->Refresh; + $self->{canvas3D}->Update; } sub GetFrame { @@ -1700,6 +1704,7 @@ sub on_config_change { my $self = shift; my ($config) = @_; + my $update_scheduled; foreach my $opt_key (@{$self->{config}->diff($config)}) { $self->{config}->set($opt_key, $config->get($opt_key)); if ($opt_key eq 'bed_shape') { @@ -1707,7 +1712,10 @@ sub on_config_change { $self->{canvas3D}->update_bed_size if $self->{canvas3D}; $self->{preview3D}->set_bed_shape($self->{config}->bed_shape) if $self->{preview3D}; - $self->update; + $update_scheduled = 1; + } elsif ($opt_key =~ '^wipe_tower' || $opt_key == 'single_extruder_multi_material') { + #$self->{canvas3D}->reload_scene if $self->{canvas3D}; + $update_scheduled = 1; } elsif ($opt_key eq 'serial_port') { if ($config->get('serial_port')) { $self->{btn_print}->Show; @@ -1732,7 +1740,8 @@ sub on_config_change { $self->{"btn_layer_editing"}->SetValue(0); } $self->{canvas3D}->layer_editing_enabled(0); - $self->{canvas3D}->update; + $self->{canvas3D}->Refresh; + $self->{canvas3D}->Update; } elsif ($self->{canvas3D}->layer_editing_allowed) { # Want to allow the layer editing, but do it only if the OpenGL supports it. if ($self->{htoolbar}) { @@ -1743,6 +1752,8 @@ sub on_config_change { } } } + + $self->update if $update_scheduled; return if !$self->GetFrame->is_loaded; @@ -1955,7 +1966,7 @@ sub refresh_canvases { my ($self) = @_; $self->{canvas}->Refresh; - $self->{canvas3D}->update if $self->{canvas3D}; + $self->{canvas3D}->reload_scene if $self->{canvas3D}; $self->{preview3D}->reload_print if $self->{preview3D}; } diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 2e108ff46..40bfb9935 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -29,13 +29,8 @@ sub new { $self->on_select(sub { my ($volume_idx) = @_; - - my $obj_idx = undef; - if ($volume_idx != -1) { - $obj_idx = $self->volumes->[$volume_idx]->object_idx; - } - $self->{on_select_object}->($obj_idx) - if $self->{on_select_object}; + $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 = @_; @@ -47,13 +42,17 @@ sub new { my $instance_idx = $volume->instance_idx; next if $done{"${obj_idx}_${instance_idx}"}; $done{"${obj_idx}_${instance_idx}"} = 1; - - 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; + 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; + } elsif ($obj_idx == 1000) { + # Move a wipe tower proxy. + } } $self->{on_instances_moved}->() @@ -88,19 +87,45 @@ sub set_on_model_update { $self->on_model_update($cb); } -sub update { +sub reload_scene { my ($self) = @_; + + if (0) { + my $i = 1; + print STDERR "3D::reload_scene - Stack Trace:\n"; + while ( (my @call_details = (caller($i++))) ){ + print STDERR $call_details[1].":".$call_details[2]." in function ".$call_details[3]."\n"; + } + } $self->reset_objects; $self->update_bed_size; foreach my $obj_idx (0..$#{$self->{model}->objects}) { my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx); - if ($self->{objects}[$obj_idx]->selected) { $self->select_volume($_) for @volume_idxs; } } + if (0) { + print "Config: $self->{config}\n"; + $self->{config}->save('d:\temp\cfg.ini'); + } + 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 * ($extruders_count - 1), + $self->{model}->bounding_box->z_max, $self->UseVBOs); + } + } } sub update_bed_size { diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index b87249501..3f246a845 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -997,8 +997,6 @@ sub build { $optgroup->append_single_option_line('filament_colour', 0); $optgroup->append_single_option_line('filament_diameter', 0); $optgroup->append_single_option_line('extrusion_multiplier', 0); - $optgroup->append_single_option_line('filament_type', 0); - $optgroup->append_single_option_line('filament_soluble', 0); $optgroup->append_single_option_line('filament_density', 0); $optgroup->append_single_option_line('filament_cost', 0); } @@ -1071,7 +1069,11 @@ sub build { { my $page = $self->add_options_page('Advanced', 'wrench.png'); { - my $optgroup = $page->new_optgroup('Print speed override'); + my $optgroup = $page->new_optgroup('Filament properties'); + $optgroup->append_single_option_line('filament_type', 0); + $optgroup->append_single_option_line('filament_soluble', 0); + + $optgroup = $page->new_optgroup('Print speed override'); $optgroup->append_single_option_line('filament_max_volumetric_speed', 0); } } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 8cccbd212..2f14bce2c 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -299,6 +299,25 @@ std::vector<int> GLVolumeCollection::load_object( return volumes_idx; } + +int GLVolumeCollection::load_wipe_tower_preview( + int obj_idx, float pos_x, float pos_y, float width, float depth, float height, bool use_VBOs) +{ + float color[4] = { 1.0f, 1.0f, 0.0f, 0.5f }; + this->volumes.emplace_back(new GLVolume(color)); + GLVolume &v = *this->volumes.back(); + auto mesh = make_cube(width, depth, height); + v.indexed_vertex_array.load_mesh_flat_shading(mesh); + v.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); + v.composite_id = obj_idx * 1000000; + v.select_group_id = obj_idx * 1000000; + v.drag_group_id = obj_idx * 1000; + return int(this->volumes.size() - 1); +} + void GLVolumeCollection::render_VBOs() const { // glEnable(GL_BLEND); @@ -453,18 +472,19 @@ static void thick_lines_to_indexed_vertex_array( idx_a[TOP] = idx_prev[TOP]; } if (ii == 0 || bottom_z_different) { + // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z. idx_a[BOTTOM] = idx_last ++; volume.push_geometry(a.x, a.y, bottom_z, 0., 0., -1.); + idx_a[LEFT ] = idx_last ++; + volume.push_geometry(a2.x, a2.y, middle_z, -xy_right_normal.x, -xy_right_normal.y, -xy_right_normal.z); + idx_a[RIGHT] = idx_last ++; + volume.push_geometry(a1.x, a1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z); } else { idx_a[BOTTOM] = idx_prev[BOTTOM]; } if (ii == 0) { // Start of the 1st line segment. - idx_a[LEFT ] = idx_last ++; - volume.push_geometry(a2.x, a2.y, middle_z, -xy_right_normal.x, -xy_right_normal.y, -xy_right_normal.z); - idx_a[RIGHT] = idx_last ++; - volume.push_geometry(a1.x, a1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z); width_initial = width; bottom_z_initial = bottom_z; memcpy(idx_initial, idx_a, sizeof(int) * 4); @@ -721,8 +741,7 @@ void _3DScene::_load_print_toolpaths( //FIXME why there are support layers? for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++ i) print_zs.push_back(float(object0->support_layers[i]->print_z)); - std::sort(print_zs.begin(), print_zs.end()); - print_zs.erase(std::unique(print_zs.begin(), print_zs.end()), print_zs.end()); + sort_remove_duplicates(print_zs); if (print_zs.size() > skirt_height) print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index ed992754d..607dff831 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -224,6 +224,9 @@ public: const std::string &select_by, const std::string &drag_by); + int load_wipe_tower_preview( + int obj_idx, float pos_x, float pos_y, float width, float depth, float height, bool use_VBOs); + // Bounding box of this volume, in unscaled coordinates. BoundingBoxf3 bounding_box; // Offset of the volume to be rendered. @@ -310,6 +313,9 @@ public: const std::string &drag_by, bool use_VBOs); + int load_wipe_tower_preview( + int obj_idx, float pos_x, float pos_y, float width, float depth, float height, bool use_VBOs); + // Render the volumes by OpenGL. void render_VBOs() const; void render_legacy() const; diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 06b042f3b..77e93a73c 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -29,8 +29,7 @@ std::vector<double> color() %code%{ RETVAL.reserve(4); RETVAL.push_back(THIS->color[0]); RETVAL.push_back(THIS->color[1]); RETVAL.push_back(THIS->color[2]); RETVAL.push_back(THIS->color[3]); %}; - int composite_id() - %code%{ RETVAL = THIS->composite_id; %}; + int select_group_id() %code%{ RETVAL = THIS->select_group_id; %}; int drag_group_id() @@ -77,6 +76,8 @@ std::vector<int> load_object(ModelObject *object, int obj_idx, std::vector<int> instance_idxs, std::string color_by, std::string select_by, std::string drag_by, bool use_VBOs); + int load_wipe_tower_preview(int obj_idx, float pos_x, float pos_y, float width, float depth, float height, bool use_VBOs); + void erase() %code{% THIS->clear(); %};