From 7d64c465c0ffdb33578f0c65a2aaea4bb2facb78 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 24 May 2017 15:20:20 +0200 Subject: [PATCH] New config field extruder_color for preview of extruder assignment. --- lib/Slic3r/GUI/3DScene.pm | 8 +- lib/Slic3r/GUI/OptionsGroup.pm | 7 ++ lib/Slic3r/GUI/OptionsGroup/Field.pm | 4 +- lib/Slic3r/GUI/Plater.pm | 132 ++++++++++++++++++++++----- lib/Slic3r/GUI/Plater/3DPreview.pm | 34 +++++-- lib/Slic3r/GUI/Tab.pm | 9 +- xs/src/libslic3r/GCode.cpp | 8 +- xs/src/libslic3r/Print.cpp | 1 + xs/src/libslic3r/PrintConfig.cpp | 13 +++ xs/src/libslic3r/PrintConfig.hpp | 4 +- xs/src/slic3r/GUI/3DScene.cpp | 104 +++++++++++++++++---- xs/src/slic3r/GUI/3DScene.hpp | 14 +-- xs/xsp/GUI_3DScene.xsp | 10 +- 13 files changed, 284 insertions(+), 64 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 6c0c726ef..636f98904 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1871,10 +1871,10 @@ sub load_object { # 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) = @_; + my ($self, $print, $colors) = @_; $self->SetCurrent($self->GetContext) if $self->UseVBOs; - Slic3r::GUI::_3DScene::_load_print_toolpaths($print, $self->volumes, $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)); } @@ -1882,10 +1882,10 @@ sub load_print_toolpaths { # 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) = @_; + my ($self, $object, $colors) = @_; $self->SetCurrent($self->GetContext) if $self->UseVBOs; - Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $self->UseVBOs); + Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $colors, $self->UseVBOs); } sub set_toolpaths_range { diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm index b519a0cb5..c323672de 100644 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ b/lib/Slic3r/GUI/OptionsGroup.pm @@ -280,6 +280,7 @@ has 'label_tooltip' => (is => 'rw', default => sub { "" }); has 'sizer' => (is => 'rw'); has 'widget' => (is => 'rw'); has '_options' => (is => 'ro', default => sub { [] }); +# Extra UI components after the label and the edit widget of the option. has '_extra_widgets' => (is => 'ro', default => sub { [] }); # this method accepts a Slic3r::GUI::OptionsGroup::Option object @@ -304,6 +305,8 @@ sub get_extra_widgets { } +# Configuration of an option. +# This very much reflects the content of the C++ ConfigOptionDef class. package Slic3r::GUI::OptionsGroup::Option; use Moo; @@ -349,6 +352,8 @@ sub get_option { my $opt_id = ($opt_index == -1 ? $opt_key : "${opt_key}#${opt_index}"); $self->_opt_map->{$opt_id} = [ $opt_key, $opt_index ]; + # Slic3r::Config::Options is a C++ Slic3r::PrintConfigDef exported as a Perl hash of hashes. + # The C++ counterpart is a constant singleton. my $optdef = $Slic3r::Config::Options->{$opt_key}; # we should access this from $self->config my $default_value = $self->_get_config_value($opt_key, $opt_index, $optdef->{gui_flags} =~ /\bserialized\b/); @@ -463,6 +468,8 @@ sub _on_kill_focus { $self->reload_config; } +# Static text shown among the options. +# Currently used for the filament cooling legend only. package Slic3r::GUI::OptionsGroup::StaticText; use Wx qw(:misc :systemsettings); use base 'Wx::StaticText'; diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm index 4d64a64ee..8f2d6b848 100644 --- a/lib/Slic3r/GUI/OptionsGroup/Field.pm +++ b/lib/Slic3r/GUI/OptionsGroup/Field.pm @@ -423,8 +423,10 @@ sub get_value { sub _string_to_colour { my ($self, $string) = @_; - + $string =~ s/^#//; + # If the color is in an invalid format, set it to white. + $string = 'FFFFFF' if ($string !~ m/^[[:xdigit:]]{6}/); return Wx::Colour->new(unpack 'C*', pack 'H*', $string); } diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 5d3fe25ba..6cf912f0c 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -10,10 +10,10 @@ use List::Util qw(sum first max); use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad rad2deg); use LWP::UserAgent; use threads::shared qw(shared_clone); -use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc +use Wx qw(:button :colour :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc :panel :sizer :toolbar :window wxTheApp :notebook :combobox wxNullBitmap); use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED - EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL + EVT_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 base 'Wx::Panel'; @@ -54,7 +54,7 @@ sub new { 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 + wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour )); # C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm $self->{model} = Slic3r::Model->new; @@ -138,7 +138,7 @@ sub new { # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { - $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}); + $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}, $self->{config}); $self->{preview3D}->canvas->on_viewport_changed(sub { $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); }); @@ -378,6 +378,7 @@ sub new { my $text = Wx::StaticText->new($self, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT); $text->SetFont($Slic3r::GUI::small_font); my $choice = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY); + EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down(0, @_); } ); $self->{preset_choosers}{$group} = [$choice]; $self->{preset_choosers_default_suppressed}{$group} = 0; # setup the listener @@ -524,12 +525,12 @@ sub _on_select_preset { $Slic3r::GUI::Settings->{presets}{"filament_${_}"} = $choice->GetString($filament_presets[$_] - $default_suppressed) for 1 .. $#filament_presets; wxTheApp->save_settings; - return; - } - - # call GetSelection() in scalar context as it's context-aware - $self->{on_select_preset}->($group, scalar($choice->GetSelection) + $default_suppressed) - if $self->{on_select_preset}; + $self->update_filament_colors_preview($choice); + } else { + # call GetSelection() in scalar context as it's context-aware + $self->{on_select_preset}->($group, scalar($choice->GetSelection) + $default_suppressed) + if $self->{on_select_preset}; + } # get new config and generate on_config_change() event for updating plater and other things $self->on_config_change($self->GetFrame->config); @@ -584,6 +585,7 @@ sub update_presets { my ($group, $presets, $default_suppressed, $selected, $is_dirty) = @_; my @choosers = @{ $self->{preset_choosers}{$group} }; + my $choice_idx = 0; foreach my $choice (@choosers) { if ($group eq 'filament' && @choosers > 1) { # if we have more than one filament chooser, keep our selection @@ -596,17 +598,7 @@ sub update_presets { next if ($preset->default && $default_suppressed); my $bitmap; if ($group eq 'filament') { - my $config = $preset->config(['filament_colour']); - my $rgb_hex = $config->filament_colour->[0]; - if ($preset->default) { - $bitmap = Wx::Bitmap->new($Slic3r::var->("spool.png"), wxBITMAP_TYPE_PNG); - } else { - $rgb_hex =~ s/^#//; - my @rgb = unpack 'C*', pack 'H*', $rgb_hex; - my $image = Wx::Image->new(16,16); - $image->SetRGB(Wx::Rect->new(0,0,16,16), @rgb); - $bitmap = Wx::Bitmap->new($image); - } + $bitmap = Wx::Bitmap->new($Slic3r::var->("spool.png"), wxBITMAP_TYPE_PNG); } elsif ($group eq 'print') { $bitmap = Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG); } elsif ($group eq 'printer') { @@ -626,9 +618,76 @@ sub update_presets { $choice->SetSelection($idx); } } + $choice_idx += 1; } $self->{preset_choosers_default_suppressed}{$group} = $default_suppressed; + $self->update_filament_colors_preview; +} + +# Update the color icon in front of each filament selection on the platter. +# If the extruder has a preview color assigned, apply the extruder color to the active selection. +# Always apply the filament color to the non-active selections. +sub update_filament_colors_preview { + my ($self, $extruder_idx) = shift; + + my @choosers = @{$self->{preset_choosers}{filament}}; + + if (ref $extruder_idx) { + # $extruder_idx is the chooser. + foreach my $chooser (@choosers) { + if ($extruder_idx == $chooser) { + $extruder_idx = $chooser; + last; + } + } + } + + my @extruder_colors = @{$self->{config}->extruder_colour}; + + my @extruder_list; + if (defined $extruder_idx) { + @extruder_list = ($extruder_idx); + } else { + # Collect extruder indices. + @extruder_list = (0..$#extruder_colors); + } + + my $filament_tab = $self->GetFrame->{options_tabs}{filament}; + my $presets = $filament_tab->{presets}; + my $default_suppressed = $filament_tab->{default_suppressed}; + + foreach my $extruder_idx (@extruder_list) { + my $chooser = $choosers[$extruder_idx]; + my $extruder_color = $self->{config}->extruder_colour->[$extruder_idx]; + my $preset_idx = 0; + my $selection_idx = $chooser->GetSelection; + foreach my $preset (@$presets) { + my $bitmap; + if ($preset->default) { + next if $default_suppressed; + } else { + # Assign an extruder color to the selected item if the extruder color is defined. + my $filament_rgb = $preset->config(['filament_colour'])->filament_colour->[0]; + my $extruder_rgb = ($preset_idx == $selection_idx && $extruder_color =~ m/^#[[:xdigit:]]{6}/) ? $extruder_color : $filament_rgb; + $filament_rgb =~ s/^#//; + $extruder_rgb =~ s/^#//; + my $image = Wx::Image->new(24,16); + if ($filament_rgb ne $extruder_rgb) { + my @rgb = unpack 'C*', pack 'H*', $extruder_rgb; + $image->SetRGB(Wx::Rect->new(0,0,16,16), @rgb); + @rgb = unpack 'C*', pack 'H*', $filament_rgb; + $image->SetRGB(Wx::Rect->new(16,0,8,16), @rgb); + } else { + my @rgb = unpack 'C*', pack 'H*', $filament_rgb; + $image->SetRGB(Wx::Rect->new(0,0,24,16), @rgb); + } + $bitmap = Wx::Bitmap->new($image); + } + $chooser->SetItemBitmap($preset_idx, $bitmap) if $bitmap; + $preset_idx += 1; + } + } } # Return a vector of indices of filaments selected by the $self->{preset_choosers}{filament} combo boxes. @@ -1675,6 +1734,8 @@ sub on_extruders_change { # initialize new choice my $choice = Wx::BitmapComboBox->new($self, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY); + my $extruder_idx = scalar @$choices; + EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down($extruder_idx, @_); } ); push @$choices, $choice; # copy icons from first choice @@ -1756,6 +1817,8 @@ sub on_config_change { $self->{"btn_layer_editing"}->Enable; } } + } elsif ($opt_key eq 'extruder_color') { + } } @@ -1793,6 +1856,33 @@ sub list_item_activated { $self->object_settings_dialog($obj_idx); } +# Called when clicked on the filament preset combo box. +# When clicked on the icon, show the color picker. +sub filament_color_box_lmouse_down +{ + my ($self, $extruder_idx, $combobox, $event) = @_; + my $pos = $event->GetLogicalPosition(Wx::ClientDC->new($combobox)); + my( $x, $y ) = ( $pos->x, $pos->y ); + if ($x > 24) { + # Let the combo box process the mouse click. + $event->Skip; + } else { + # Swallow the mouse click and open the color picker. + my $data = Wx::ColourData->new; + $data->SetChooseFull(1); + my $dialog = Wx::ColourDialog->new($self->GetFrame, $data); + if ($dialog->ShowModal == wxID_OK) { + my $cfg = Slic3r::Config->new; + my $colors = $self->GetFrame->config->get('extruder_colour'); + $colors->[$extruder_idx] = $dialog->GetColourData->GetColour->GetAsString(wxC2S_HTML_SYNTAX); + $cfg->set('extruder_colour', $colors); + $self->GetFrame->{options_tabs}{printer}->load_config($cfg); + $self->update_filament_colors_preview($extruder_idx); + } + $dialog->Destroy(); + } +} + sub object_cut_dialog { my $self = shift; my ($obj_idx) = @_; diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index fef8bc197..573f29b1c 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -8,14 +8,15 @@ use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE); use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX); use base qw(Wx::Panel Class::Accessor); -__PACKAGE__->mk_accessors(qw(print enabled _loaded canvas slider_low slider_high single_layer)); +__PACKAGE__->mk_accessors(qw(print enabled _loaded canvas slider_low slider_high single_layer color_by_extruder)); sub new { my $class = shift; - my ($parent, $print) = @_; + my ($parent, $print, $config) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition); - + $self->{config} = $config; + # init GUI elements my $canvas = Slic3r::GUI::3DScene->new($self); $canvas->use_plain_shader(1); @@ -53,7 +54,9 @@ sub new { $z_label_high->SetFont($Slic3r::GUI::small_font); $self->single_layer(0); + $self->color_by_extruder(0); my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, "1 Layer"); + my $checkbox_color_by_extruder = $self->{checkbox_color_by_extruder} = Wx::CheckBox->new($self, -1, "Tool"); my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL); my $vsizer = Wx::BoxSizer->new(wxVERTICAL); @@ -67,6 +70,7 @@ sub new { $hsizer->Add($vsizer, 0, wxEXPAND, 0); $vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0); $vsizer_outer->Add($checkbox_singlelayer, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5); + $vsizer_outer->Add($checkbox_color_by_extruder, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5); my $sizer = Wx::BoxSizer->new(wxHORIZONTAL); $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0); @@ -107,6 +111,10 @@ sub new { $self->set_z_idx_high($slider_high->GetValue); } }); + EVT_CHECKBOX($self, $checkbox_color_by_extruder, sub { + $self->color_by_extruder($checkbox_color_by_extruder->GetValue()); + $self->reload_print; + }); $self->SetSizer($sizer); $self->SetMinSize($self->GetSize); @@ -180,13 +188,27 @@ sub load_print { $self->slider_low->Show; $self->slider_high->Show; $self->Layout; - + + # Collect colors per extruder. + # Leave it empty, if the print should be colored by a feature. + my @colors = (); + if ($self->color_by_extruder) { + my @extruder_colors = @{$self->{config}->extruder_colour}; + my @filament_colors = @{$self->{config}->filament_colour}; + for (my $i = 0; $i <= $#extruder_colors; $i += 1) { + my $color = $extruder_colors[$i]; + $color = $filament_colors[$i] if ($color !~ m/^#[[:xdigit:]]{6}/); + $color = '#FFFFFF' if ($color !~ m/^#[[:xdigit:]]{6}/); + push @colors, $color; + } + } + if ($self->IsShown) { # load skirt and brim - $self->canvas->load_print_toolpaths($self->print); + $self->canvas->load_print_toolpaths($self->print, \@colors); foreach my $object (@{$self->print->objects}) { - $self->canvas->load_print_object_toolpaths($object); + $self->canvas->load_print_object_toolpaths($object, \@colors); # Show the objects in very transparent color. #my @volume_ids = $self->canvas->load_object($object->model_object); diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 9009264e1..c4746bbc4 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -1164,7 +1164,7 @@ sub build { nozzle_diameter extruder_offset retract_length retract_lift retract_speed deretract_speed retract_before_wipe retract_restart_extra retract_before_travel retract_layer_change wipe retract_length_toolchange retract_restart_extra_toolchange - printer_notes + extruder_colour printer_notes )); $self->{config}->set('printer_settings_id', ''); @@ -1455,7 +1455,7 @@ sub _extruder_options { qw(nozzle_diameter min_layer_height max_layer_height extruder_offset retract_length retract_lift retract_lift_above retract_lift_below retract_speed deretract_speed retract_before_wipe retract_restart_extra retract_before_travel wipe - retract_layer_change retract_length_toolchange retract_restart_extra_toolchange) } + retract_layer_change retract_length_toolchange retract_restart_extra_toolchange extruder_colour) } sub _build_extruder_pages { my $self = shift; @@ -1514,6 +1514,10 @@ sub _build_extruder_pages { $optgroup->append_single_option_line($_, $extruder_idx) for qw(retract_length_toolchange retract_restart_extra_toolchange); } + { + my $optgroup = $page->new_optgroup('Preview'); + $optgroup->append_single_option_line('extruder_colour', $extruder_idx); + } } # remove extra pages @@ -1765,6 +1769,7 @@ sub get_name { package Slic3r::GUI::Tab::Preset; use Moo; +# The preset represents a "default" set of properties. has 'default' => (is => 'ro', default => sub { 0 }); has 'external' => (is => 'ro', default => sub { 0 }); has 'name' => (is => 'rw', required => 1); diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index ba0679793..52067259e 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -293,6 +293,9 @@ std::vector GCode::collect_layers_to_print(const PrintObjec return layers_to_print; } +// Prepare for non-sequential printing of multiple objects: Support resp. object layers with nearly identical print_z +// will be printed for all objects at once. +// Return a list of items. std::vector>> GCode::collect_layers_to_print(const Print &print) { struct OrderingItem { @@ -304,11 +307,12 @@ std::vector>> GCode::collec std::vector ordering; for (size_t i = 0; i < print.objects.size(); ++ i) { per_object[i] = collect_layers_to_print(*print.objects[i]); - const LayerToPrint &front = per_object[i].front(); OrderingItem ordering_item; ordering_item.object_idx = i; + ordering.reserve(ordering.size() + per_object[i].size()); + const LayerToPrint &front = per_object[i].front(); for (const LayerToPrint <p : per_object[i]) { - ordering_item.print_z = ltp.print_z(); + ordering_item.print_z = ltp.print_z(); ordering_item.layer_idx = <p - &front; ordering.emplace_back(ordering_item); } diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 5d739d9ba..cd2b22963 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -142,6 +142,7 @@ Print::invalidate_state_by_config_options(const std::vector || *opt_key == "end_gcode" || *opt_key == "extruder_clearance_height" || *opt_key == "extruder_clearance_radius" + || *opt_key == "extruder_colour" || *opt_key == "extruder_offset" || *opt_key == "extrusion_axis" || *opt_key == "extrusion_multiplier" diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index c4b0df611..7e1e5b77b 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -243,6 +243,19 @@ PrintConfigDef::PrintConfigDef() def->min = 0; def->default_value = new ConfigOptionFloat(20); + def = this->add("extruder_colour", coStrings); + def->label = "Extruder Color"; + def->tooltip = "This is only used in the Slic3r interface as a visual help."; + def->cli = "extruder-color=s@"; + def->gui_type = "color"; + { + ConfigOptionStrings* opt = new ConfigOptionStrings(); + // Empty string means no color assigned yet. +// opt->values.push_back("#FFFFFF"); + opt->values.push_back(""); + def->default_value = opt; + } + def = this->add("extruder_offset", coPoints); def->label = "Extruder offset"; def->tooltip = "If your firmware doesn't handle the extruder displacement you need the G-code to take it into account. This option lets you specify the displacement of each extruder with respect to the first one. It expects positive coordinates (they will be subtracted from the XY coordinate)."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index cac7b3ad2..d7d7bcbb1 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -413,7 +413,7 @@ public: // This object is mapped to Perl as Slic3r::Config::Print. class PrintConfig : public GCodeConfig { - public: +public: ConfigOptionBool avoid_crossing_perimeters; ConfigOptionPoints bed_shape; ConfigOptionInt bed_temperature; @@ -427,6 +427,7 @@ class PrintConfig : public GCodeConfig ConfigOptionFloat duplicate_distance; ConfigOptionFloat extruder_clearance_height; ConfigOptionFloat extruder_clearance_radius; + ConfigOptionStrings extruder_colour; ConfigOptionPoints extruder_offset; ConfigOptionBool fan_always_on; ConfigOptionInt fan_below_layer_time; @@ -491,6 +492,7 @@ class PrintConfig : public GCodeConfig OPT_PTR(duplicate_distance); OPT_PTR(extruder_clearance_height); OPT_PTR(extruder_clearance_radius); + OPT_PTR(extruder_colour); OPT_PTR(extruder_offset); OPT_PTR(fan_always_on); OPT_PTR(fan_below_layer_time); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 2f14bce2c..591a584ed 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -710,12 +710,40 @@ void _3DScene::_glew_init() glewInit(); } +inline int hex_digit_to_int(const char c) +{ + return + (c >= '0' && c <= '9') ? int(c - '0') : + (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : + (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; +} + +inline std::vector parse_colors(const std::vector &scolors) +{ + std::vector output(scolors.size() * 4, 1.f); + for (size_t i = 0; i < scolors.size(); ++ i) { + const std::string &scolor = scolors[i]; + const char *c = scolor.data() + 1; + if (scolor.size() == 7 && scolor.front() == '#') { + for (size_t j = 0; j < 3; ++j) { + int digit1 = hex_digit_to_int(*c ++); + int digit2 = hex_digit_to_int(*c ++); + if (digit1 == -1 || digit2 == -1) + break; + output[i * 4 + j] = float(digit1 * 16 + digit2) / 255.f; + } + } + } + return output; +} + // Create 3D thick extrusion lines for a skirt and brim. // Adds a new Slic3r::GUI::3DScene::Volume to volumes. void _3DScene::_load_print_toolpaths( - const Print *print, - GLVolumeCollection *volumes, - bool use_VBOs) + const Print *print, + GLVolumeCollection *volumes, + const std::vector &tool_colors, + bool use_VBOs) { if (! print->has_skirt() && print->config.brim_width.value == 0) return; @@ -765,10 +793,13 @@ void _3DScene::_load_print_toolpaths( // Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, // one for perimeters, one for infill and one for supports. void _3DScene::_load_print_object_toolpaths( - const PrintObject *print_object, - GLVolumeCollection *volumes, - bool use_VBOs) + const PrintObject *print_object, + GLVolumeCollection *volumes, + const std::vector &tool_colors_str, + bool use_VBOs) { + std::vector tool_colors = parse_colors(tool_colors_str); + struct Ctxt { const Points *shifted_copies; @@ -778,6 +809,7 @@ void _3DScene::_load_print_object_toolpaths( bool has_perimeters; bool has_infill; bool has_support; + const std::vector* tool_colors; // Number of vertices (each vertex is 6x4=24 bytes long) static const size_t alloc_size_max () { return 131072; } // 3.15MB @@ -788,6 +820,11 @@ void _3DScene::_load_print_object_toolpaths( static const float* color_perimeters () { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow static const float* color_infill () { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish + + // For cloring by a tool, return a parsed color. + bool color_by_tool() const { return tool_colors != nullptr; } + size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } + const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } } ctxt; ctxt.shifted_copies = &print_object->_shifted_copies; @@ -811,6 +848,7 @@ void _3DScene::_load_print_object_toolpaths( ctxt.has_perimeters = print_object->state.is_done(posPerimeters); ctxt.has_infill = print_object->state.is_done(posInfill); ctxt.has_support = print_object->state.is_done(posSupportMaterial); + ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; @@ -829,15 +867,20 @@ void _3DScene::_load_print_object_toolpaths( tbb::parallel_for( tbb::blocked_range(0, ctxt.layers.size(), grain_size), [&ctxt, &new_volume](const tbb::blocked_range& range) { - GLVolume* vols[3] = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; - for (size_t i = 0; i < 3; ++ i) { + std::vector vols; + if (ctxt.color_by_tool()) { + for (size_t i = 0; i < ctxt.number_tools(); ++ i) + vols.emplace_back(new_volume(ctxt.color_tool(i))); + } else + vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; + for (size_t i = 0; i < vols.size(); ++ i) { GLVolume &volume = *vols[i]; volume.bounding_box = ctxt.bbox; volume.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); } for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { const Layer *layer = ctxt.layers[idx_layer]; - for (size_t i = 0; i < 3; ++ i) { + for (size_t i = 0; i < vols.size(); ++ i) { GLVolume &vol = *vols[i]; if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) { vol.print_zs.push_back(layer->print_z); @@ -847,18 +890,45 @@ void _3DScene::_load_print_object_toolpaths( } for (const Point ©: *ctxt.shifted_copies) { for (const LayerRegion *layerm : layer->regions) { - if (ctxt.has_perimeters) - extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, *vols[0]); - if (ctxt.has_infill) - extrusionentity_to_verts(layerm->fills, float(layer->print_z), copy, *vols[1]); + if (ctxt.has_perimeters) { + int volume_idx = ctxt.color_by_tool() ? + std::max(layerm->region()->config.perimeter_extruder.value - 1, 0) : + 0; + extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, *vols[volume_idx]); + } + if (ctxt.has_infill) { + for (const ExtrusionEntity *ee : layerm->fills.entities) { + // fill represents infill extrusions of a single island. + const auto *fill = dynamic_cast(ee); + if (fill->entities.empty()) + // This shouldn't happen but first_point() would fail. + continue; + int volume_idx = ctxt.color_by_tool() ? + std::max(0, + (is_solid_infill(fill->entities.front()->role()) ? + layerm->region()->config.solid_infill_extruder : + layerm->region()->config.infill_extruder) - 1) : + 1; + extrusionentity_to_verts(*fill, float(layer->print_z), copy, *vols[volume_idx]); + } + } } if (ctxt.has_support) { const SupportLayer *support_layer = dynamic_cast(layer); - if (support_layer) - extrusionentity_to_verts(support_layer->support_fills, float(layer->print_z), copy, *vols[2]); + if (support_layer) { + for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) { + int volume_idx = ctxt.color_by_tool() ? + std::max(0, + ((extrusion_entity->role() == erSupportMaterial) ? + support_layer->object()->config.support_material_extruder : + support_layer->object()->config.support_material_interface_extruder) - 1) : + 2; + extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, *vols[volume_idx]); + } + } } } - for (size_t i = 0; i < 3; ++ i) { + for (size_t i = 0; i < vols.size(); ++ i) { GLVolume &vol = *vols[i]; if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { // Store the vertex arrays and restart their containers, @@ -876,7 +946,7 @@ void _3DScene::_load_print_object_toolpaths( } } } - for (size_t i = 0; i < 3; ++ i) + for (size_t i = 0; i < vols.size(); ++ i) vols[i]->indexed_vertex_array.shrink_to_fit(); }); diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp index 607dff831..1996ca965 100644 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ b/xs/src/slic3r/GUI/3DScene.hpp @@ -344,14 +344,16 @@ public: static void _glew_init(); static void _load_print_toolpaths( - const Print *print, - GLVolumeCollection *volumes, - bool use_VBOs); + const Print *print, + GLVolumeCollection *volumes, + const std::vector &tool_colors, + bool use_VBOs); static void _load_print_object_toolpaths( - const PrintObject *print_object, - GLVolumeCollection *volumes, - bool use_VBOs); + const PrintObject *print_object, + GLVolumeCollection *volumes, + const std::vector &tool_colors, + bool use_VBOs); }; } diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp index 77e93a73c..06d4b8c19 100644 --- a/xs/xsp/GUI_3DScene.xsp +++ b/xs/xsp/GUI_3DScene.xsp @@ -118,19 +118,21 @@ _glew_init() _3DScene::_glew_init(); void -_load_print_toolpaths(print, volumes, use_VBOs) +_load_print_toolpaths(print, volumes, tool_colors, use_VBOs) Print *print; GLVolumeCollection *volumes; + std::vector tool_colors; int use_VBOs; CODE: - _3DScene::_load_print_toolpaths(print, volumes, use_VBOs != 0); + _3DScene::_load_print_toolpaths(print, volumes, tool_colors, use_VBOs != 0); void -_load_print_object_toolpaths(print_object, volumes, use_VBOs) +_load_print_object_toolpaths(print_object, volumes, tool_colors, use_VBOs) PrintObject *print_object; GLVolumeCollection *volumes; + std::vector tool_colors; int use_VBOs; CODE: - _3DScene::_load_print_object_toolpaths(print_object, volumes, use_VBOs != 0); + _3DScene::_load_print_object_toolpaths(print_object, volumes, tool_colors, use_VBOs != 0); %}