Merge branch 'master' into updating

This commit is contained in:
Vojtech Kral 2018-04-16 17:26:02 +02:00
commit 214ad2925b
52 changed files with 3331 additions and 1146 deletions

View file

@ -1,27 +0,0 @@
# generated by Slic3r Prusa Edition 1.38.4 on 2017-12-20 at 11:02:03
bed_temperature = 0
bridge_fan_speed = 100
compatible_printers =
compatible_printers_condition =
cooling = 1
disable_fan_first_layers = 3
end_filament_gcode = "; Filament-specific end gcode \n;END gcode for filament\n"
extrusion_multiplier = 1
fan_always_on = 0
fan_below_layer_time = 60
filament_colour = #29b2b2
filament_cost = 0
filament_density = 0
filament_diameter = 3
filament_max_volumetric_speed = 0
filament_notes = ""
filament_soluble = 0
filament_type = PLA
first_layer_bed_temperature = 0
first_layer_temperature = 205
max_fan_speed = 100
min_fan_speed = 35
min_print_speed = 10
slowdown_below_layer_time = 5
start_filament_gcode = "; Filament gcode\n"
temperature = 200

View file

@ -1527,10 +1527,13 @@ sub draw_active_object_annotations {
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', $volume->bounding_box->z_max * $z_cursor_relative);
$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,
@ -1552,8 +1555,8 @@ sub draw_active_object_annotations {
glBegin(GL_QUADS);
glVertex3f($bar_left, $bar_bottom, 0);
glVertex3f($bar_right, $bar_bottom, 0);
glVertex3f($bar_right, $bar_top, $volume->bounding_box->z_max);
glVertex3f($bar_left, $bar_top, $volume->bounding_box->z_max);
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;
@ -1572,7 +1575,6 @@ sub draw_active_object_annotations {
# Paint the graph.
#FIXME show some kind of legend.
my $print_object = $self->{print}->get_object($object_idx);
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');
@ -1677,6 +1679,11 @@ sub draw_warning {
}
}
sub update_volumes_colors_by_extruder {
my ($self, $config) = @_;
$self->volumes->update_colors_by_extruder($config);
}
sub opengl_info
{
my ($self, %params) = @_;
@ -1823,7 +1830,6 @@ sub _fragment_shader_Gouraud {
return <<'FRAGMENT';
#version 110
const vec4 OUTSIDE_COLOR = vec4(0.24, 0.42, 0.62, 1.0);
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
// x = tainted, y = specular;
@ -1836,11 +1842,9 @@ uniform vec4 uniform_color;
void main()
{
// if the fragment is outside the print volume use predefined color
vec4 color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? OUTSIDE_COLOR : uniform_color;
gl_FragColor = vec4(intensity.y, intensity.y, intensity.y, 0.0) + color * intensity.x;
gl_FragColor.a = color.a;
// 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
@ -1932,8 +1936,6 @@ const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074);
#define INTENSITY_AMBIENT 0.3
uniform float z_to_texture_row;
// x = tainted, y = specular;
varying vec2 intensity;

View file

@ -52,9 +52,8 @@ sub new {
$self->{config} = Slic3r::Config::new_from_defaults_keys([qw(
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile
nozzle_diameter single_extruder_multi_material
wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour
max_print_height
nozzle_diameter single_extruder_multi_material wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width
wipe_tower_rotation_angle extruder_colour filament_colour max_print_height
)]);
# C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm
$self->{model} = Slic3r::Model->new;
@ -105,7 +104,6 @@ sub new {
$self->{btn_reslice}->Enable($enable);
$self->{btn_print}->Enable($enable);
$self->{btn_send_gcode}->Enable($enable);
$self->{btn_export_stl}->Enable($enable);
};
# Initialize 3D plater
@ -738,8 +736,14 @@ sub load_model_objects {
{
# if the object is too large (more than 5 times the bed), scale it down
my $size = $o->bounding_box->size;
my $ratio = max(@$size[X,Y]) / unscale(max(@$bed_size[X,Y]));
if ($ratio > 5) {
my $ratio = max($size->x / unscale($bed_size->x), $size->y / unscale($bed_size->y));
if ($ratio > 10000) {
# the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
# so scale down the mesh
$o->scale_xyz(Slic3r::Pointf3->new(1/$ratio, 1/$ratio, 1/$ratio));
$scaled_down = 1;
}
elsif ($ratio > 5) {
$_->set_scaling_factor(1/$ratio) for @{$o->instances};
$scaled_down = 1;
}
@ -1915,7 +1919,8 @@ sub object_list_changed {
}
my $export_in_progress = $self->{export_gcode_output_file} || $self->{send_gcode_file};
my $method = ($have_objects && ! $export_in_progress) ? 'Enable' : 'Disable';
my $model_fits = $self->{model}->fits_print_volume($self->{config});
my $method = ($have_objects && ! $export_in_progress && $model_fits) ? 'Enable' : 'Disable';
$self->{"btn_$_"}->$method
for grep $self->{"btn_$_"}, qw(reslice export_gcode print send_gcode);
}

View file

@ -210,12 +210,15 @@ sub reload_scene {
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);
$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)
{
@ -229,6 +232,9 @@ sub reload_scene {
Slic3r::GUI::_3DScene::reset_warning_texture();
$self->on_enable_action_buttons->(1) if ($self->on_enable_action_buttons);
}
} else {
$self->set_warning_enabled(0);
Slic3r::GUI::_3DScene::reset_warning_texture();
}
}

View file

@ -60,7 +60,7 @@ sub new {
label => 'Z',
default => $self->{cut_options}{z},
min => 0,
max => $self->{model_object}->bounding_box->size->z * $self->{model_object}->instances->[0]->scaling_factor,
max => $self->{model_object}->bounding_box->size->z,
full_width => 1,
));
{
@ -247,6 +247,7 @@ sub _update {
$self->{cut_options}{z},
[@expolygons],
);
$self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->{config});
$self->{canvas}->Render;
}
}

View file

@ -9,7 +9,7 @@ use utf8;
use File::Basename qw(basename);
use Wx qw(:misc :sizer :treectrl :button :keycode wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL wxMOD_CONTROL
wxTheApp);
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN);
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN EVT_KEY_DOWN);
use base 'Wx::Panel';
use constant ICON_OBJECT => 0;
@ -88,7 +88,7 @@ sub new {
$self->{btn_move_down}->SetFont($Slic3r::GUI::small_font);
# part settings panel
$self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; });
$self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; $self->_update_canvas; });
my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL);
$settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0);
@ -162,6 +162,7 @@ sub new {
$canvas->load_object($self->{model_object}, undef, undef, [0]);
$canvas->set_auto_bed_shape;
$canvas->SetSize([500,700]);
$canvas->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config});
$canvas->zoom_to_volumes;
}
@ -190,6 +191,14 @@ sub new {
EVT_BUTTON($self, $self->{btn_split}, \&on_btn_split);
EVT_BUTTON($self, $self->{btn_move_up}, \&on_btn_move_up);
EVT_BUTTON($self, $self->{btn_move_down}, \&on_btn_move_down);
EVT_KEY_DOWN($canvas, sub {
my ($canvas, $event) = @_;
if ($event->GetKeyCode == WXK_DELETE) {
$canvas->GetParent->on_btn_delete;
} else {
$event->Skip;
}
});
$self->reload_tree;
@ -400,14 +409,17 @@ sub on_tree_key_down {
my ($self, $event) = @_;
my $keycode = $event->GetKeyCode;
# Wx >= 0.9911
if (defined(&Wx::TreeEvent::GetKeyEvent) &&
($event->GetKeyEvent->GetModifiers & wxMOD_CONTROL)) {
if ($keycode == WXK_UP) {
$event->Skip;
$self->on_btn_move_up;
} elsif ($keycode == WXK_DOWN) {
$event->Skip;
$self->on_btn_move_down;
if (defined(&Wx::TreeEvent::GetKeyEvent)) {
if ($event->GetKeyEvent->GetModifiers & wxMOD_CONTROL) {
if ($keycode == WXK_UP) {
$event->Skip;
$self->on_btn_move_up;
} elsif ($keycode == WXK_DOWN) {
$event->Skip;
$self->on_btn_move_down;
}
} elsif ($keycode == WXK_DELETE) {
$self->on_btn_delete;
}
}
}
@ -478,6 +490,7 @@ sub _parts_changed {
$self->{canvas}->reset_objects;
$self->{canvas}->load_object($self->{model_object});
$self->{canvas}->zoom_to_volumes;
$self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config});
$self->{canvas}->Render;
}
}
@ -508,6 +521,25 @@ sub PartSettingsChanged {
return $self->{part_settings_changed};
}
sub _update_canvas {
my ($self) = @_;
if ($self->{canvas}) {
$self->{canvas}->reset_objects;
$self->{canvas}->load_object($self->{model_object});
# restore selection, if any
if (my $itemData = $self->get_selection) {
if ($itemData->{type} eq 'volume') {
$self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1);
}
}
$self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config});
$self->{canvas}->Render;
}
}
sub _update {
my ($self) = @_;
my ($m_x, $m_y, $m_z) = ($self->{move_options}{x}, $self->{move_options}{y}, $self->{move_options}{z});
@ -528,6 +560,7 @@ sub _update {
push @objects, $self->{model_object};
$self->{canvas}->reset_objects;
$self->{canvas}->load_object($_, undef, [0]) for @objects;
$self->{canvas}->update_volumes_colors_by_extruder($self->GetParent->GetParent->GetParent->{config});
$self->{canvas}->Render;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 616 B

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 B

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 454 B

After

Width:  |  Height:  |  Size: 419 B

View file

@ -600,6 +600,7 @@ temperature = 255
[filament:*FLEX*]
inherits = *common*
bed_temperature = 50
bridge_fan_speed = 100
compatible_printers_condition = nozzle_diameter[0]>0.35 and num_extruders==1
cooling = 0
@ -950,6 +951,7 @@ variable_layer_height = 0
[printer:Original Prusa i3 MK2]
inherits = *common*
default_print_profile = 0.15mm OPTIMAL
[printer:Original Prusa i3 MK2 0.25 nozzle]
inherits = *common*
@ -968,6 +970,7 @@ max_layer_height = 0.35
min_layer_height = 0.1
nozzle_diameter = 0.6
printer_variant = 0.6
default_print_profile = 0.20mm NORMAL 0.6 nozzle
[printer:Original Prusa i3 MK2 MM Single Mode]
inherits = *mm-single*
@ -976,6 +979,7 @@ inherits = *mm-single*
inherits = *mm-single*
nozzle_diameter = 0.6
printer_variant = 0.6
default_print_profile = 0.20mm NORMAL 0.6 nozzle
[printer:Original Prusa i3 MK2 MultiMaterial]
inherits = *mm-multi*

View file

@ -57,7 +57,7 @@ plan tests => 8;
my $config = Slic3r::Config::new_from_defaults;
$config->set('layer_height', 0.2);
$config->set('first_layer_height', 0.2);
$config->set('nozzle_diameter', [0.5]);
$config->set('nozzle_diameter', [0.5,0.5,0.5,0.5]);
$config->set('infill_every_layers', 2);
$config->set('perimeter_extruder', 1);
$config->set('infill_extruder', 2);

View file

@ -49,7 +49,7 @@ use Slic3r::Test;
my $parser = Slic3r::GCode::PlaceholderParser->new;
my $config = Slic3r::Config::new_from_defaults;
$config->set('printer_notes', ' PRINTER_VENDOR_PRUSA3D PRINTER_MODEL_MK2 ');
$config->set('nozzle_diameter', [0.6, 0.6, 0.6, 0.6]);
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$parser->apply_config($config);
$parser->set('foo' => 0);
$parser->set('bar' => 2);
@ -123,6 +123,7 @@ use Slic3r::Test;
{
my $config = Slic3r::Config->new;
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$config->set('extruder', 2);
$config->set('first_layer_temperature', [200,205]);
@ -204,6 +205,7 @@ use Slic3r::Test;
{
my $config = Slic3r::Config->new;
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6,0.6]);
$config->set('start_gcode', qq!
;substitution:{if infill_extruder==1}if block
{elsif infill_extruder==2}elsif block 1
@ -228,6 +230,7 @@ use Slic3r::Test;
{
my $config = Slic3r::Config->new;
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$config->set('start_gcode',
';substitution:{if infill_extruder==1}{if perimeter_extruder==1}block11{else}block12{endif}' .
'{elsif infill_extruder==2}{if perimeter_extruder==1}block21{else}block22{endif}' .

View file

@ -164,6 +164,7 @@ SKIP:
for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
my $config = Slic3r::Config::new_from_defaults;
$config->set('nozzle_diameter', [0.4,0.4,0.4,0.4]);
$config->set('fill_pattern', $pattern);
$config->set('external_fill_pattern', $pattern);
$config->set('perimeters', 1);
@ -195,6 +196,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
{
my $config = Slic3r::Config::new_from_defaults;
$config->set('nozzle_diameter', [0.4,0.4,0.4,0.4]);
$config->set('infill_only_where_needed', 1);
$config->set('bottom_solid_layers', 0);
$config->set('infill_extruder', 2);
@ -276,7 +278,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
$config->set('fill_density', 0);
$config->set('layer_height', 0.2);
$config->set('first_layer_height', 0.2);
$config->set('nozzle_diameter', [0.35]);
$config->set('nozzle_diameter', [0.35,0.35,0.35,0.35]);
$config->set('infill_extruder', 2);
$config->set('solid_infill_extruder', 2);
$config->set('infill_extrusion_width', 0.52);

View file

@ -16,6 +16,7 @@ use Slic3r::Test;
{
my $config = Slic3r::Config::new_from_defaults;
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$config->set('raft_layers', 2);
$config->set('infill_extruder', 2);
$config->set('solid_infill_extruder', 3);
@ -89,6 +90,7 @@ use Slic3r::Test;
{
my $config = Slic3r::Config::new_from_defaults;
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$config->set('support_material_extruder', 3);
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
@ -97,6 +99,7 @@ use Slic3r::Test;
{
my $config = Slic3r::Config->new;
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$config->set('extruder', 2);
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
@ -105,6 +108,7 @@ use Slic3r::Test;
{
my $config = Slic3r::Config->new;
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$config->set('perimeter_extruder', 2);
$config->set('infill_extruder', 2);
$config->set('support_material_extruder', 2);
@ -126,6 +130,7 @@ use Slic3r::Test;
$upper_config->set('bottom_solid_layers', 1);
$upper_config->set('top_solid_layers', 0);
my $config = Slic3r::Config::new_from_defaults;
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$config->set('fill_density', 0);
$config->set('solid_infill_speed', 99);
$config->set('top_solid_infill_speed', 99);
@ -172,6 +177,7 @@ use Slic3r::Test;
my $object = $model->objects->[0];
my $config = Slic3r::Config::new_from_defaults;
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$config->set('layer_height', 0.4);
$config->set('first_layer_height', '100%');
$config->set('skirts', 0);

View file

@ -95,6 +95,7 @@ use Slic3r::Test qw(_eq);
1;
};
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$config->set('first_layer_height', $config->layer_height);
$config->set('first_layer_speed', '100%');
$config->set('start_gcode', ''); # to avoid dealing with the nozzle lift in start G-code
@ -207,6 +208,7 @@ use Slic3r::Test qw(_eq);
{
my $config = Slic3r::Config::new_from_defaults;
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$config->set('start_gcode', '');
$config->set('retract_lift', [3, 4]);
@ -255,4 +257,4 @@ use Slic3r::Test qw(_eq);
'Z is not lifted above the configured value for 2. extruder';
}
__END__
__END__

View file

@ -206,6 +206,10 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/GUI/2DBed.hpp
${LIBDIR}/slic3r/GUI/wxExtensions.cpp
${LIBDIR}/slic3r/GUI/wxExtensions.hpp
${LIBDIR}/slic3r/GUI/WipeTowerDialog.cpp
${LIBDIR}/slic3r/GUI/WipeTowerDialog.hpp
${LIBDIR}/slic3r/GUI/RammingChart.cpp
${LIBDIR}/slic3r/GUI/RammingChart.hpp
${LIBDIR}/slic3r/GUI/BonjourDialog.cpp
${LIBDIR}/slic3r/GUI/BonjourDialog.hpp
${LIBDIR}/slic3r/Config/Snapshot.cpp

View file

@ -9,74 +9,115 @@
namespace Slic3r {
static inline Polyline make_wave_vertical(
double width, double height, double x0,
double segmentSize, double scaleFactor,
double z_cos, double z_sin, bool flip)
static inline double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
{
Polyline polyline;
polyline.points.emplace_back(Point(coord_t(clamp(0., width, x0) * scaleFactor), 0));
double phase_offset_sin = (z_cos < 0 ? M_PI : 0) + M_PI;
double phase_offset_cos = (z_cos < 0 ? M_PI : 0) + M_PI + (flip ? M_PI : 0.);
for (double y = 0.; y < height + segmentSize; y += segmentSize) {
y = std::min(y, height);
double a = sin(y + phase_offset_sin);
if (vertical) {
double phase_offset = (z_cos < 0 ? M_PI : 0) + M_PI;
double a = sin(x + phase_offset);
double b = - z_cos;
double res = z_sin * cos(y + phase_offset_cos);
double res = z_sin * cos(x + phase_offset + (flip ? M_PI : 0.));
double r = sqrt(sqr(a) + sqr(b));
double x = clamp(0., width, asin(a/r) + asin(res/r) + M_PI + x0);
polyline.points.emplace_back(convert_to<Point>(Pointf(x, y) * scaleFactor));
return asin(a/r) + asin(res/r) + M_PI;
}
if (flip)
std::reverse(polyline.points.begin(), polyline.points.end());
else {
double phase_offset = z_sin < 0 ? M_PI : 0.;
double a = cos(x + phase_offset);
double b = - z_sin;
double res = z_cos * sin(x + phase_offset + (flip ? 0 : M_PI));
double r = sqrt(sqr(a) + sqr(b));
return (asin(a/r) + asin(res/r) + 0.5 * M_PI);
}
}
static inline Polyline make_wave(
const std::vector<Pointf>& one_period, double width, double height, double offset, double scaleFactor,
double z_cos, double z_sin, bool vertical)
{
std::vector<Pointf> points = one_period;
double period = points.back().x;
points.pop_back();
int n = points.size();
do {
points.emplace_back(Pointf(points[points.size()-n].x + period, points[points.size()-n].y));
} while (points.back().x < width);
points.back().x = width;
// and construct the final polyline to return:
Polyline polyline;
for (auto& point : points) {
point.y += offset;
point.y = clamp(0., height, double(point.y));
if (vertical)
std::swap(point.x, point.y);
polyline.points.emplace_back(convert_to<Point>(point * scaleFactor));
}
return polyline;
}
static inline Polyline make_wave_horizontal(
double width, double height, double y0,
double segmentSize, double scaleFactor,
double z_cos, double z_sin, bool flip)
static std::vector<Pointf> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip)
{
Polyline polyline;
polyline.points.emplace_back(Point(0, coord_t(clamp(0., height, y0) * scaleFactor)));
double phase_offset_sin = (z_sin < 0 ? M_PI : 0) + (flip ? 0 : M_PI);
double phase_offset_cos = z_sin < 0 ? M_PI : 0.;
for (double x = 0.; x < width + segmentSize; x += segmentSize) {
x = std::min(x, width);
double a = cos(x + phase_offset_cos);
double b = - z_sin;
double res = z_cos * sin(x + phase_offset_sin);
double r = sqrt(sqr(a) + sqr(b));
double y = clamp(0., height, asin(a/r) + asin(res/r) + 0.5 * M_PI + y0);
polyline.points.emplace_back(convert_to<Point>(Pointf(x, y) * scaleFactor));
std::vector<Pointf> points;
double dx = M_PI_4; // very coarse spacing to begin with
double limit = std::min(2*M_PI, width);
for (double x = 0.; x < limit + EPSILON; x += dx) { // so the last point is there too
x = std::min(x, limit);
points.emplace_back(Pointf(x,f(x, z_sin,z_cos, vertical, flip)));
}
if (flip)
std::reverse(polyline.points.begin(), polyline.points.end());
return polyline;
// now we will check all internal points and in case some are too far from the line connecting its neighbours,
// we will add one more point on each side:
const double tolerance = .1;
for (unsigned int i=1;i<points.size()-1;++i) {
auto& lp = points[i-1]; // left point
auto& tp = points[i]; // this point
auto& rp = points[i+1]; // right point
// calculate distance of the point to the line:
double dist_mm = unscale(scaleFactor * std::abs( (rp.y - lp.y)*tp.x + (lp.x - rp.x)*tp.y + (rp.x*lp.y - rp.y*lp.x) ) / std::hypot((rp.y - lp.y),(lp.x - rp.x)));
if (dist_mm > tolerance) { // if the difference from straight line is more than this
double x = 0.5f * (points[i-1].x + points[i].x);
points.emplace_back(Pointf(x, f(x, z_sin, z_cos, vertical, flip)));
x = 0.5f * (points[i+1].x + points[i].x);
points.emplace_back(Pointf(x, f(x, z_sin, z_cos, vertical, flip)));
std::sort(points.begin(), points.end()); // we added the points to the end, but need them all in order
--i; // decrement i so we also check the first newly added point
}
}
return points;
}
static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
{
double scaleFactor = scale_(line_spacing) / density_adjusted;
double segmentSize = 0.5 * density_adjusted;
const double scaleFactor = scale_(line_spacing) / density_adjusted;
//scale factor for 5% : 8 712 388
// 1z = 10^-6 mm ?
double z = gridZ / scaleFactor;
double z_sin = sin(z);
double z_cos = cos(z);
Polylines result;
if (std::abs(z_sin) <= std::abs(z_cos)) {
// Vertical wave
double x0 = M_PI * (int)((- 0.5 * M_PI) / M_PI - 1.);
bool flip = ((int)(x0 / M_PI + 1.) & 1) != 0;
for (; x0 < width - 0.5 * M_PI; x0 += M_PI, flip = ! flip)
result.emplace_back(make_wave_vertical(width, height, x0, segmentSize, scaleFactor, z_cos, z_sin, flip));
} else {
// Horizontal wave
bool flip = true;
for (double y0 = 0.; y0 < height; y0 += M_PI, flip = !flip)
result.emplace_back(make_wave_horizontal(width, height, y0, segmentSize, scaleFactor, z_cos, z_sin, flip));
const double z = gridZ / scaleFactor;
const double z_sin = sin(z);
const double z_cos = cos(z);
bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
double lower_bound = 0.;
double upper_bound = height;
bool flip = true;
if (vertical) {
flip = false;
lower_bound = -M_PI;
upper_bound = width - M_PI_2;
std::swap(width,height);
}
std::vector<Pointf> one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time
Polylines result;
for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates odd polylines
result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
flip = !flip; // even polylines are a bit shifted
one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // updates the one period sample
for (double y0 = lower_bound + M_PI; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates even polylines
result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
return result;
}
@ -90,13 +131,13 @@ void FillGyroid::_fill_surface_single(
// no rotation is supported for this infill pattern (yet)
BoundingBox bb = expolygon.contour.bounding_box();
// Density adjusted to have a good %of weight.
double density_adjusted = params.density * 1.75;
double density_adjusted = std::max(0., params.density * 2.);
// Distance between the gyroid waves in scaled coordinates.
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
// align bounding box to a multiple of our grid module
bb.merge(_align_to_grid(bb.min, Point(2.*M_PI*distance, 2.*M_PI*distance)));
// generate pattern
Polylines polylines = make_gyroid_waves(
scale_(this->z),

View file

@ -388,7 +388,6 @@ namespace Slic3r {
bool _create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter);
void _apply_transform(ModelObject& object, const Matrix4x4& matrix);
void _apply_transform(ModelInstance& instance, const Matrix4x4& matrix);
bool _handle_start_config(const char** attributes, unsigned int num_attributes);
@ -557,19 +556,6 @@ namespace Slic3r {
if (!_generate_volumes(*object.second, obj_geometry->second, *volumes_ptr))
return false;
// apply transformation if the object contains a single instance
if (object.second->instances.size() == 1)
{
for (const Instance& instance : m_instances)
{
if (object.second->instances[0] == instance.instance)
{
_apply_transform(*object.second, instance.matrix);
break;
}
}
}
}
// fixes the min z of the model if negative
@ -822,10 +808,9 @@ namespace Slic3r {
if (instance.instance != nullptr)
{
ModelObject* object = instance.instance->get_object();
if ((object != nullptr) && (object->instances.size() > 1))
if (object != nullptr)
{
// multiple instances -> apply the matrix to the instance
// (for single instance the transformation can be applied only after the volumes have been generated)
// apply the matrix to the instance
_apply_transform(*instance.instance, instance.matrix);
}
}
@ -1120,15 +1105,6 @@ namespace Slic3r {
return true;
}
void _3MF_Importer::_apply_transform(ModelObject& object, const Matrix4x4& matrix)
{
float matrix3x4[12] = { matrix(0, 0), matrix(0, 1), matrix(0, 2), matrix(0, 3),
matrix(1, 0), matrix(1, 1), matrix(1, 2), matrix(1, 3),
matrix(2, 0), matrix(2, 1), matrix(2, 2), matrix(2, 3) };
object.transform(matrix3x4);
}
void _3MF_Importer::_apply_transform(ModelInstance& instance, const Matrix4x4& matrix)
{
// slic3r ModelInstance cannot be transformed using a matrix
@ -1645,9 +1621,7 @@ namespace Slic3r {
}
Eigen::Affine3f transform;
transform = Eigen::Translation3f((float)(instance->offset.x + object.origin_translation.x), (float)(instance->offset.y + object.origin_translation.y), (float)object.origin_translation.z)
* Eigen::AngleAxisf((float)instance->rotation, Eigen::Vector3f::UnitZ())
* Eigen::Scaling((float)instance->scaling_factor);
transform = Eigen::Translation3f((float)instance->offset.x, (float)instance->offset.y, 0.0f) * Eigen::AngleAxisf((float)instance->rotation, Eigen::Vector3f::UnitZ()) * Eigen::Scaling((float)instance->scaling_factor);
build_items.emplace_back(instance_id, transform.matrix());
stream << " </" << OBJECT_TAG << ">\n";
@ -1690,10 +1664,9 @@ namespace Slic3r {
for (int i = 0; i < stl.stats.shared_vertices; ++i)
{
stream << " <" << VERTEX_TAG << " ";
// Subtract origin_translation in order to restore the original local coordinates
stream << "x=\"" << (stl.v_shared[i].x - object.origin_translation.x) << "\" ";
stream << "y=\"" << (stl.v_shared[i].y - object.origin_translation.y) << "\" ";
stream << "z=\"" << (stl.v_shared[i].z - object.origin_translation.z) << "\" />\n";
stream << "x=\"" << stl.v_shared[i].x << "\" ";
stream << "y=\"" << stl.v_shared[i].y << "\" ";
stream << "z=\"" << stl.v_shared[i].z << "\" />\n";
}
}

View file

@ -17,12 +17,26 @@ public:
struct xy
{
xy(float x = 0.f, float y = 0.f) : x(x), y(y) {}
xy(const xy& pos,float xp,float yp) : x(pos.x+xp), y(pos.y+yp) {}
xy operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; }
xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; }
bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
// Rotate the point around given point about given angle (in degrees)
// shifts the result so that point of rotation is in the middle of the tower
xy rotate(const xy& origin, float width, float depth, float angle) const {
xy out(0,0);
float temp_x = x - width / 2.f;
float temp_y = y - depth / 2.f;
angle *= M_PI/180.;
out.x += (temp_x - origin.x) * cos(angle) - (temp_y - origin.y) * sin(angle);
out.y += (temp_x - origin.x) * sin(angle) + (temp_y - origin.y) * cos(angle);
return out + origin;
}
float x;
float y;
};
@ -112,17 +126,15 @@ public:
const std::vector<unsigned int> &tools,
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
bool last_wipe_inside_wipe_tower,
// May be used by a stand alone post processor.
Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
bool last_wipe_inside_wipe_tower) = 0;
// Returns gcode for toolchange and the end position.
// if new_tool == -1, just unload the current filament over the wipe tower.
virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer, Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer) = 0;
// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
// Call this method only if layer_finished() is false.
virtual ToolChangeResult finish_layer(Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
virtual ToolChangeResult finish_layer() = 0;
// Is the current layer finished? A layer is finished if either the wipe tower is finished, or
// the wipe tower has been completely covered by the tool change extrusions,

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,14 @@
#ifndef WipeTowerPrusaMM_hpp_
#define WipeTowerPrusaMM_hpp_
#include <algorithm>
#include <cmath>
#include <string>
#include <sstream>
#include <utility>
#include "WipeTower.hpp"
namespace Slic3r
{
@ -15,6 +16,8 @@ namespace PrusaMultiMaterial {
class Writer;
};
class WipeTowerPrusaMM : public WipeTower
{
public:
@ -39,65 +42,98 @@ public:
// y -- y coordinates of wipe tower in mm ( left bottom corner )
// 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 wipe_area, unsigned int initial_tool) :
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) :
m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_area(wipe_area),
m_wipe_tower_rotation_angle(rotation_angle),
m_y_shift(0.f),
m_z_pos(0.f),
m_current_tool(initial_tool)
m_is_first_layer(false),
m_cooling_tube_retraction(cooling_tube_retraction),
m_cooling_tube_length(cooling_tube_length),
m_parking_pos_retraction(parking_pos_retraction),
m_bridging(bridging),
m_current_tool(initial_tool)
{
for (size_t i = 0; i < 4; ++ i) {
// Extruder specific parameters.
m_material[i] = PLA;
m_temperature[i] = 0;
m_first_layer_temperature[i] = 0;
}
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));
}
virtual ~WipeTowerPrusaMM() {}
// _retract - retract value in mm
void set_retract(float retract) { m_retract = retract; }
// _zHop - z hop value in mm
void set_zhop(float zhop) { m_zhop = zhop; }
// Set the extruder properties.
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp)
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
float unloading_speed, float delay, float cooling_time, std::string ramming_parameters, float nozzle_diameter)
{
m_material[idx] = material;
m_temperature[idx] = temp;
m_first_layer_temperature[idx] = first_layer_temp;
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
m_filpar.push_back(FilamentParameters());
m_filpar[idx].material = material;
m_filpar[idx].temperature = temp;
m_filpar[idx].first_layer_temperature = first_layer_temp;
m_filpar[idx].loading_speed = loading_speed;
m_filpar[idx].unloading_speed = unloading_speed;
m_filpar[idx].delay = delay;
m_filpar[idx].cooling_time = cooling_time;
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
std::stringstream stream{ramming_parameters};
float speed = 0.f;
stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
m_filpar[idx].ramming_line_width_multiplicator /= 100;
m_filpar[idx].ramming_step_multiplicator /= 100;
while (stream >> speed)
m_filpar[idx].ramming_speed.push_back(speed);
}
// 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);
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
// Switch to a next layer.
virtual void set_layer(
// Print height of this layer.
float print_z,
// Layer height, used to calculate extrusion the rate.
float layer_height,
float print_z,
// Layer height, used to calculate extrusion the rate.
float layer_height,
// Maximum number of tool changes on this layer or the layers below.
size_t max_tool_changes,
size_t max_tool_changes,
// Is this the first layer of the print? In that case print the brim first.
bool is_first_layer,
bool is_first_layer,
// Is this the last layer of the waste tower?
bool is_last_layer)
bool is_last_layer)
{
m_z_pos = print_z;
m_layer_height = layer_height;
m_max_color_changes = max_tool_changes;
m_is_first_layer = is_first_layer;
m_is_last_layer = is_last_layer;
// Start counting the color changes from zero. Special case: -1 - extrude a brim first.
m_idx_tool_change_in_layer = is_first_layer ? (unsigned int)(-1) : 0;
m_current_wipe_start_y = 0.f;
m_print_brim = is_first_layer;
m_depth_traversed = 0.f;
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
++ m_num_layer_changes;
// Extrusion rate for an extrusion aka perimeter width 0.35mm.
// Clamp the extrusion height to a 0.2mm layer height, independent of the nozzle diameter.
// m_extrusion_flow = std::min(0.2f, layer_height) * 0.145f;
// Use a strictly
m_extrusion_flow = layer_height * 0.145f;
if (is_first_layer) {
this->m_num_layer_changes = 0;
this->m_num_tool_changes = 0;
}
else
++ m_num_layer_changes;
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
m_extrusion_flow = extrusion_flow(layer_height);
// Advance m_layer_info iterator, making sure we got it right
while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
++m_layer_info;
}
// Return the wipe tower position.
@ -115,79 +151,114 @@ public:
const std::vector<unsigned int> &tools,
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
bool last_wipe_inside_wipe_tower,
// May be used by a stand alone post processor.
Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE);
bool last_wipe_inside_wipe_tower);
// Returns gcode for a toolchange and a final print head position.
// On the first layer, extrude a brim around the future wipe tower first.
virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer, Purpose purpose);
virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
// Fill the unfilled space with a sparse infill.
// Call this method only if layer_finished() is false.
virtual ToolChangeResult finish_layer(Purpose purpose);
virtual ToolChangeResult finish_layer();
// Is the current layer finished?
virtual bool layer_finished() const {
return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
}
// Is the current layer finished? A layer is finished if either the wipe tower is finished, or
// the wipe tower has been completely covered by the tool change extrusions,
// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
virtual bool layer_finished() const
{ return m_idx_tool_change_in_layer == m_max_color_changes; }
private:
WipeTowerPrusaMM();
// A fill-in direction (positive Y, negative Y) alternates with each layer.
enum wipe_shape
enum wipe_shape // A fill-in direction
{
SHAPE_NORMAL = 1,
SHAPE_NORMAL = 1,
SHAPE_REVERSED = -1
};
// Left front corner of the wipe tower in mm.
xy m_wipe_tower_pos;
// Width of the wipe tower.
float m_wipe_tower_width;
// Per color Y span.
float m_wipe_area;
// Current Z position.
float m_z_pos = 0.f;
// Current layer height.
float m_layer_height = 0.f;
// Maximum number of color changes per layer.
size_t m_max_color_changes = 0;
// Is this the 1st layer of the print? If so, print the brim around the waste tower.
bool m_is_first_layer = false;
// Is this the last layer of this waste tower?
bool m_is_last_layer = false;
const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
const float Filament_Area = M_PI * 1.75f * 1.75f / 4.f; // filament area in mm^2
const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
const float WT_EPSILON = 1e-3f;
xy m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
float m_wipe_tower_width; // Width of the wipe tower.
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
float m_y_shift = 0.f; // y shift passed to writer
float m_z_pos = 0.f; // Current Z position.
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.
// G-code generator parameters.
float m_zhop = 0.5f;
float m_retract = 4.f;
// Width of an extrusion line, also a perimeter spacing for 100% infill.
float m_perimeter_width = 0.5f;
// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
float m_extrusion_flow = 0.029f;
float m_cooling_tube_retraction = 0.f;
float m_cooling_tube_length = 0.f;
float m_parking_pos_retraction = 0.f;
float m_bridging = 0.f;
bool m_adhesion = true;
float m_perimeter_width = 0.4 * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
float m_extrusion_flow = 0.038; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
struct FilamentParameters {
material_type material = PLA;
int temperature = 0;
int first_layer_temperature = 0;
float loading_speed = 0.f;
float unloading_speed = 0.f;
float delay = 0.f ;
float cooling_time = 0.f;
float ramming_line_width_multiplicator = 0.f;
float ramming_step_multiplicator = 0.f;
std::vector<float> ramming_speed;
float nozzle_diameter;
};
// Extruder specific parameters.
material_type m_material[4];
int m_temperature[4];
int m_first_layer_temperature[4];
std::vector<FilamentParameters> m_filpar;
// State of the wiper tower generator.
// Layer change counter for the output statistics.
unsigned int m_num_layer_changes = 0;
// Tool change change counter for the output statistics.
unsigned int m_num_tool_changes = 0;
// Layer change counter in this layer. Counting up to m_max_color_changes.
unsigned int m_idx_tool_change_in_layer = 0;
// State of the wipe tower generator.
unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics.
///unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
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 = 0;
// Current y position at the wipe tower.
float m_current_wipe_start_y = 0.f;
// How much to wipe the 1st extruder over the wipe tower at the 1st layer
// after the wipe tower brim has been extruded?
float m_initial_extra_wipe = 0.f;
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
{
if ( layer_height < 0 )
return m_extrusion_flow;
return layer_height * ( m_perimeter_width - layer_height * (1-M_PI/4.f)) / Filament_Area;
}
// 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.)));
}
// Calculates depth for all layers and propagates them downwards
void plan_tower();
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
void make_wipe_tower_square();
// Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
void save_on_last_wipe();
struct box_coordinates
{
@ -216,14 +287,42 @@ private:
}
xy ld; // left down
xy lu; // left upper
xy ru; // right upper
xy rd; // right lower
xy ru; // right upper
};
// to store information about tool changes for a given layer
struct WipeTowerInfo{
struct ToolChange {
unsigned int old_tool;
unsigned int new_tool;
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 z; // z position of the layer
float height; // layer height
float depth; // depth of the layer based on all layers above
float extra_spacing;
float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
std::vector<ToolChange> tool_changes;
WipeTowerInfo(float z_par, float layer_height_par)
: z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
};
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
// Returns gcode for wipe tower brim
// sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
// sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
// offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower
ToolChangeResult toolchange_Brim(Purpose purpose, bool sideOnly = false, float y_offset = 0.f);
ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
void toolchange_Unload(
PrusaMultiMaterial::Writer &writer,
@ -243,11 +342,12 @@ private:
void toolchange_Wipe(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box,
bool skip_initial_y_move);
void toolchange_Perimeter();
float wipe_volume);
};
}; // namespace Slic3r
#endif /* WipeTowerPrusaMM_hpp_ */

View file

@ -16,6 +16,8 @@
namespace Slic3r {
unsigned int Model::s_auto_extruder_id = 1;
Model::Model(const Model &other)
{
// copy materials
@ -405,10 +407,19 @@ void Model::convert_multipart_object()
ModelObject* object = new ModelObject(this);
object->input_file = this->objects.front()->input_file;
reset_auto_extruder_id();
for (const ModelObject* o : this->objects)
for (const ModelVolume* v : o->volumes)
object->add_volume(*v)->name = o->name;
{
ModelVolume* new_v = object->add_volume(*v);
if (new_v != nullptr)
{
new_v->name = o->name;
new_v->config.set_deserialize("extruder", get_auto_extruder_id_as_string());
}
}
for (const ModelInstance* i : this->objects.front()->instances)
object->add_instance(*i);
@ -466,6 +477,28 @@ bool Model::fits_print_volume(const FullPrintConfig &config) const
return print_volume.contains(transformed_bounding_box());
}
unsigned int Model::get_auto_extruder_id()
{
unsigned int id = s_auto_extruder_id;
if (++s_auto_extruder_id > 4)
reset_auto_extruder_id();
return id;
}
std::string Model::get_auto_extruder_id_as_string()
{
char str_extruder[64];
sprintf(str_extruder, "%ud", get_auto_extruder_id());
return str_extruder;
}
void Model::reset_auto_extruder_id()
{
s_auto_extruder_id = 1;
}
ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) :
name(other.name),
input_file(other.input_file),
@ -830,33 +863,8 @@ void ModelObject::cut(coordf_t z, Model* model) const
lower->add_volume(*volume);
} else {
TriangleMesh upper_mesh, lower_mesh;
// TODO: shouldn't we use object bounding box instead of per-volume bb?
coordf_t cut_z = z + volume->mesh.bounding_box().min.z;
if (false) {
// if (volume->mesh.has_multiple_patches()) {
// Cutting algorithm does not work on intersecting meshes.
// As we are not sure whether the meshes don't intersect,
// we rather split the mesh into multiple non-intersecting pieces.
TriangleMeshPtrs meshptrs = volume->mesh.split();
for (TriangleMeshPtrs::iterator mesh = meshptrs.begin(); mesh != meshptrs.end(); ++mesh) {
printf("Cutting mesh patch %d of %d\n", int(mesh - meshptrs.begin()), int(meshptrs.size()));
(*mesh)->repair();
TriangleMeshSlicer tms(*mesh);
if (mesh == meshptrs.begin()) {
tms.cut(cut_z, &upper_mesh, &lower_mesh);
} else {
TriangleMesh upper_mesh_this, lower_mesh_this;
tms.cut(cut_z, &upper_mesh_this, &lower_mesh_this);
upper_mesh.merge(upper_mesh_this);
lower_mesh.merge(lower_mesh_this);
}
delete *mesh;
}
} else {
printf("Cutting mesh patch\n");
TriangleMeshSlicer tms(&volume->mesh);
tms.cut(cut_z, &upper_mesh, &lower_mesh);
}
TriangleMeshSlicer tms(&volume->mesh);
tms.cut(z, &upper_mesh, &lower_mesh);
upper_mesh.repair();
lower_mesh.repair();
@ -995,6 +1003,9 @@ size_t ModelVolume::split()
size_t idx = 0;
size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
std::string name = this->name;
Model::reset_auto_extruder_id();
for (TriangleMesh *mesh : meshptrs) {
mesh->repair();
if (idx == 0)
@ -1004,6 +1015,7 @@ size_t ModelVolume::split()
char str_idx[64];
sprintf(str_idx, "_%d", idx + 1);
this->object->volumes[ivolume]->name = name + str_idx;
this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string());
delete mesh;
++ idx;
}

View file

@ -230,6 +230,8 @@ private:
// all objects may share mutliple materials.
class Model
{
static unsigned int s_auto_extruder_id;
public:
// Materials are owned by a model and referenced by objects through t_model_material_id.
// Single material may be shared by multiple models.
@ -288,6 +290,10 @@ public:
bool fits_print_volume(const FullPrintConfig &config) const;
void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
static unsigned int get_auto_extruder_id();
static std::string get_auto_extruder_id_as_string();
static void reset_auto_extruder_id();
};
}

View file

@ -183,6 +183,11 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "filament_type"
|| opt_key == "filament_soluble"
|| opt_key == "first_layer_temperature"
|| opt_key == "filament_loading_speed"
|| opt_key == "filament_unloading_speed"
|| opt_key == "filament_toolchange_delay"
|| opt_key == "filament_cooling_time"
|| opt_key == "filament_ramming_parameters"
|| opt_key == "gcode_flavor"
|| opt_key == "single_extruder_multi_material"
|| opt_key == "spiral_vase"
@ -191,7 +196,12 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "wipe_tower_x"
|| opt_key == "wipe_tower_y"
|| opt_key == "wipe_tower_width"
|| opt_key == "wipe_tower_per_color_wipe"
|| opt_key == "wipe_tower_rotation_angle"
|| opt_key == "wipe_tower_bridging"
|| opt_key == "wiping_volumes_matrix"
|| opt_key == "parking_pos_retraction"
|| opt_key == "cooling_tube_retraction"
|| opt_key == "cooling_tube_length"
|| opt_key == "z_offset") {
steps.emplace_back(psWipeTower);
} else if (
@ -571,6 +581,12 @@ std::string Print::validate() const
return "The Spiral Vase option can only be used when printing single material objects.";
}
if (this->config.single_extruder_multi_material) {
for (size_t i=1; i<this->config.nozzle_diameter.values.size(); ++i)
if (this->config.nozzle_diameter.values[i] != this->config.nozzle_diameter.values[i-1])
return "All extruders must have the same diameter for single extruder multimaterial printer.";
}
if (this->has_wipe_tower() && ! this->objects.empty()) {
#if 0
for (auto dmr : this->config.nozzle_diameter.values)
@ -582,6 +598,12 @@ std::string Print::validate() const
if (! this->config.use_relative_e_distances)
return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).";
SlicingParameters slicing_params0 = this->objects.front()->slicing_parameters();
const PrintObject* most_layered_object = this->objects.front(); // object with highest layer_height_profile.size() encountered so far
for (const auto* object : objects)
if (object->layer_height_profile.size() > most_layered_object->layer_height_profile.size())
most_layered_object = object;
for (PrintObject *object : this->objects) {
SlicingParameters slicing_params = object->slicing_parameters();
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
@ -596,10 +618,24 @@ std::string Print::validate() const
bool was_layer_height_profile_valid = object->layer_height_profile_valid;
object->update_layer_height_profile();
object->layer_height_profile_valid = was_layer_height_profile_valid;
for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)
if ( this->config.variable_layer_height ) {
int i = 0;
while ( i < object->layer_height_profile.size() ) {
if (std::abs(most_layered_object->layer_height_profile[i] - object->layer_height_profile[i]) > EPSILON)
return "The Wipe tower is only supported if all objects have the same layer height profile";
++i;
if (i == object->layer_height_profile.size()-2) // this element contains the objects max z, if the other object is taller,
// it does not have to match - we will step over it
if (most_layered_object->layer_height_profile[i] > object->layer_height_profile[i])
++i;
}
}
/*for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)
if (object->layer_height_profile[i-1] > slicing_params.object_print_z_min + EPSILON &&
std::abs(object->layer_height_profile[i] - object->config.layer_height) > EPSILON)
return "The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed.";
return "The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed.";*/
}
}
@ -613,7 +649,12 @@ std::string Print::validate() const
for (unsigned int extruder_id : extruders)
nozzle_diameters.push_back(this->config.nozzle_diameter.get_at(extruder_id));
double min_nozzle_diameter = *std::min_element(nozzle_diameters.begin(), nozzle_diameters.end());
unsigned int total_extruders_count = this->config.nozzle_diameter.size();
for (const auto& extruder_idx : extruders)
if ( extruder_idx >= total_extruders_count )
return "One or more object were assigned an extruder that the printer does not have.";
for (PrintObject *object : this->objects) {
if ((object->config.support_material_extruder == -1 || object->config.support_material_interface_extruder == -1) &&
(object->config.raft_layers > 0 || object->config.support_material.value)) {
@ -1026,22 +1067,33 @@ 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_per_color_wipe.value),
m_tool_ordering.first_extruder());
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());
//wipe_tower.set_retract();
//wipe_tower.set_zhop();
// Set the extruder & material properties at the wipe tower object.
for (size_t i = 0; i < 4; ++ i)
for (size_t i = 0; i < (int)(sqrt(wiping_volumes.size())+EPSILON); ++ i)
wipe_tower.set_extruder(
i,
WipeTowerPrusaMM::parse_material(this->config.filament_type.get_at(i).c_str()),
this->config.temperature.get_at(i),
this->config.first_layer_temperature.get_at(i));
this->config.first_layer_temperature.get_at(i),
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_time.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.
@ -1049,12 +1101,37 @@ void Print::_make_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, WipeTower::PURPOSE_EXTRUDE));
wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), ! last_priming_wipe_full));
// 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
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());
current_extruder_id = extruder_id;
}
}
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();
/*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.
@ -1098,7 +1175,7 @@ void Print::_make_wipe_tower()
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;
@ -1117,7 +1194,7 @@ void Print::_make_wipe_tower()
wipe_tower.set_layer(float(m_tool_ordering.back().print_z), float(layer_height), 0, false, true);
}
m_wipe_tower_final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
wipe_tower.tool_change((unsigned int)-1, false, WipeTower::PURPOSE_EXTRUDE));
wipe_tower.tool_change((unsigned int)-1, false));
}
std::string Print::output_filename()

View file

@ -166,6 +166,22 @@ PrintConfigDef::PrintConfigDef()
def->cli = "cooling!";
def->default_value = new ConfigOptionBools { true };
def = this->add("cooling_tube_retraction", coFloat);
def->label = L("Cooling tube position");
def->tooltip = L("Distance of the center-point of the cooling tube from the extruder tip ");
def->sidetext = L("mm");
def->cli = "cooling_tube_retraction=f";
def->min = 0;
def->default_value = new ConfigOptionFloat(91.5f);
def = this->add("cooling_tube_length", coFloat);
def->label = L("Cooling tube length");
def->tooltip = L("Length of the cooling tube to limit space for cooling moves inside it ");
def->sidetext = L("mm");
def->cli = "cooling_tube_length=f";
def->min = 0;
def->default_value = new ConfigOptionFloat(5.f);
def = this->add("default_acceleration", coFloat);
def->label = L("Default");
def->tooltip = L("This is the acceleration your printer will be reset to after "
@ -439,6 +455,49 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloats { 0. };
def = this->add("filament_loading_speed", coFloats);
def->label = L("Loading speed");
def->tooltip = L("Speed used for loading the filament on the wipe tower. ");
def->sidetext = L("mm/s");
def->cli = "filament-loading-speed=f@";
def->min = 0;
def->default_value = new ConfigOptionFloats { 28. };
def = this->add("filament_unloading_speed", coFloats);
def->label = L("Unloading speed");
def->tooltip = L("Speed used for unloading the filament on the wipe tower (does not affect "
" initial part of unloading just after ramming). ");
def->sidetext = L("mm/s");
def->cli = "filament-unloading-speed=f@";
def->min = 0;
def->default_value = new ConfigOptionFloats { 90. };
def = this->add("filament_toolchange_delay", coFloats);
def->label = L("Delay after unloading");
def->tooltip = L("Time to wait after the filament is unloaded. "
"May help to get reliable toolchanges with flexible materials "
"that may need more time to shrink to original dimensions. ");
def->sidetext = L("s");
def->cli = "filament-toolchange-delay=f@";
def->min = 0;
def->default_value = new ConfigOptionFloats { 0. };
def = this->add("filament_cooling_time", coFloats);
def->label = L("Cooling time");
def->tooltip = L("The filament is slowly moved back and forth after retraction into the cooling tube "
"for this amount of time.");
def->cli = "filament_cooling_time=i@";
def->sidetext = L("s");
def->min = 0;
def->default_value = new ConfigOptionFloats { 14.f };
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 ");
def->cli = "filament-ramming-parameters=s@";
def->default_value = new ConfigOptionStrings { "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0|"
" 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" };
def = this->add("filament_diameter", coFloats);
def->label = L("Diameter");
def->tooltip = L("Enter your filament diameter here. Good precision is required, so use a caliper "
@ -977,6 +1036,15 @@ PrintConfigDef::PrintConfigDef()
def->cli = "overhangs!";
def->default_value = new ConfigOptionBool(true);
def = this->add("parking_pos_retraction", coFloat);
def->label = L("Filament parking position");
def->tooltip = L("Distance of the extruder tip from the position where the filament is parked "
"when unloaded. This should match the value in printer firmware. ");
def->sidetext = L("mm");
def->cli = "parking_pos_retraction=f";
def->min = 0;
def->default_value = new ConfigOptionFloat(92.f);
def = this->add("perimeter_acceleration", coFloat);
def->label = L("Perimeters");
def->tooltip = L("This is the acceleration your printer will use for perimeters. "
@ -1744,6 +1812,25 @@ PrintConfigDef::PrintConfigDef()
def->cli = "wipe-tower!";
def->default_value = new ConfigOptionBool(false);
def = this->add("wiping_volumes_extruders", coFloats);
def->label = L("Purging volumes - load/unload volumes");
def->tooltip = L("This vector saves required volumes to change from/to each tool used on the "
"wipe tower. These values are used to simplify creation of the full purging "
"volumes below. ");
def->cli = "wiping-volumes-extruders=f@";
def->default_value = new ConfigOptionFloats { 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f };
def = this->add("wiping_volumes_matrix", coFloats);
def->label = L("Purging volumes - matrix");
def->tooltip = L("This matrix describes volumes (in cubic milimetres) required to purge the"
" new filament on the wipe tower for any given pair of tools. ");
def->cli = "wiping-volumes-matrix=f@";
def->default_value = new ConfigOptionFloats { 0.f, 140.f, 140.f, 140.f, 140.f,
140.f, 0.f, 140.f, 140.f, 140.f,
140.f, 140.f, 0.f, 140.f, 140.f,
140.f, 140.f, 140.f, 0.f, 140.f,
140.f, 140.f, 140.f, 140.f, 0.f };
def = this->add("wipe_tower_x", coFloat);
def->label = L("Position X");
def->tooltip = L("X coordinate of the left front corner of a wipe tower");
@ -1765,14 +1852,19 @@ PrintConfigDef::PrintConfigDef()
def->cli = "wipe-tower-width=f";
def->default_value = new ConfigOptionFloat(60.);
def = this->add("wipe_tower_per_color_wipe", coFloat);
def->label = L("Per color change depth");
def->tooltip = L("Depth of a wipe color per color change. For N colors, there will be "
"maximum (N-1) tool switches performed, therefore the total depth "
"of the wipe tower will be (N-1) times this value.");
def = this->add("wipe_tower_rotation_angle", coFloat);
def->label = L("Wipe tower rotation angle");
def->tooltip = L("Wipe tower rotation angle with respect to x-axis ");
def->sidetext = L("degrees");
def->cli = "wipe-tower-rotation-angle=f";
def->default_value = new ConfigOptionFloat(0.);
def = this->add("wipe_tower_bridging", coFloat);
def->label = L("Maximal bridging distance");
def->tooltip = L("Maximal distance between supports on sparse infill sections. ");
def->sidetext = L("mm");
def->cli = "wipe-tower-per-color-wipe=f";
def->default_value = new ConfigOptionFloat(15.);
def->cli = "wipe-tower-bridging=f";
def->default_value = new ConfigOptionFloat(10.);
def = this->add("xy_size_compensation", coFloat);
def->label = L("XY Size Compensation");
@ -1852,8 +1944,9 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
"seal_position", "vibration_limit", "bed_size", "octoprint_host",
"print_center", "g0", "threads", "pressure_advance"
"print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe"
};
if (ignore.find(opt_key) != ignore.end()) {
opt_key = "";
return;

View file

@ -154,6 +154,13 @@ public:
// Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned.
std::string validate();
// Verify whether the opt_key has not been obsoleted or renamed.
// Both opt_key and value may be modified by handle_legacy().
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
// handle_legacy() is called internally by set_deserialize().
void handle_legacy(t_config_option_key &opt_key, std::string &value) const override
{ PrintConfigDef::handle_legacy(opt_key, value); }
};
template<typename CONFIG>
@ -466,6 +473,11 @@ public:
ConfigOptionBools filament_soluble;
ConfigOptionFloats filament_cost;
ConfigOptionFloats filament_max_volumetric_speed;
ConfigOptionFloats filament_loading_speed;
ConfigOptionFloats filament_unloading_speed;
ConfigOptionFloats filament_toolchange_delay;
ConfigOptionFloats filament_cooling_time;
ConfigOptionStrings filament_ramming_parameters;
ConfigOptionBool gcode_comments;
ConfigOptionEnum<GCodeFlavor> gcode_flavor;
ConfigOptionString layer_gcode;
@ -491,7 +503,11 @@ public:
ConfigOptionBool use_relative_e_distances;
ConfigOptionBool use_volumetric_e;
ConfigOptionBool variable_layer_height;
ConfigOptionFloat cooling_tube_retraction;
ConfigOptionFloat cooling_tube_length;
ConfigOptionFloat parking_pos_retraction;
std::string get_extrusion_axis() const
{
return
@ -515,6 +531,11 @@ protected:
OPT_PTR(filament_soluble);
OPT_PTR(filament_cost);
OPT_PTR(filament_max_volumetric_speed);
OPT_PTR(filament_loading_speed);
OPT_PTR(filament_unloading_speed);
OPT_PTR(filament_toolchange_delay);
OPT_PTR(filament_cooling_time);
OPT_PTR(filament_ramming_parameters);
OPT_PTR(gcode_comments);
OPT_PTR(gcode_flavor);
OPT_PTR(layer_gcode);
@ -540,6 +561,9 @@ protected:
OPT_PTR(use_relative_e_distances);
OPT_PTR(use_volumetric_e);
OPT_PTR(variable_layer_height);
OPT_PTR(cooling_tube_retraction);
OPT_PTR(cooling_tube_length);
OPT_PTR(parking_pos_retraction);
}
};
@ -610,6 +634,10 @@ public:
ConfigOptionFloat wipe_tower_y;
ConfigOptionFloat wipe_tower_width;
ConfigOptionFloat wipe_tower_per_color_wipe;
ConfigOptionFloat wipe_tower_rotation_angle;
ConfigOptionFloat wipe_tower_bridging;
ConfigOptionFloats wiping_volumes_matrix;
ConfigOptionFloats wiping_volumes_extruders;
ConfigOptionFloat z_offset;
protected:
@ -675,6 +703,10 @@ protected:
OPT_PTR(wipe_tower_y);
OPT_PTR(wipe_tower_width);
OPT_PTR(wipe_tower_per_color_wipe);
OPT_PTR(wipe_tower_rotation_angle);
OPT_PTR(wipe_tower_bridging);
OPT_PTR(wiping_volumes_matrix);
OPT_PTR(wiping_volumes_extruders);
OPT_PTR(z_offset);
}
};
@ -713,6 +745,7 @@ class FullPrintConfig :
public:
// Validate the FullPrintConfig. Returns an empty string on success, otherwise an error message is returned.
std::string validate();
protected:
// Protected constructor to be called to initialize ConfigCache::m_default.
FullPrintConfig(int) : PrintObjectConfig(0), PrintRegionConfig(0), PrintConfig(0), HostConfig(0) {}

View file

@ -6,7 +6,7 @@ namespace GUI {
class Bed_2D : public wxPanel
{
bool m_user_drawn_background = false;
bool m_user_drawn_background = true;
bool m_painted = false;
bool m_interactive = false;
@ -26,7 +26,9 @@ public:
{
Create(parent, wxID_ANY, wxDefaultPosition, wxSize(250, -1), wxTAB_TRAVERSAL);
// m_user_drawn_background = $^O ne 'darwin';
m_user_drawn_background = true;
#ifdef __APPLE__
m_user_drawn_background = false;
#endif /*__APPLE__*/
Bind(wxEVT_PAINT, ([this](wxPaintEvent e) { repaint(); }));
// EVT_ERASE_BACKGROUND($self, sub{}) if $self->{user_drawn_background};
// Bind(EVT_MOUSE_EVENTS, ([this](wxMouseEvent event){/*mouse_event()*/; }));

View file

@ -9,6 +9,7 @@
#include "../../libslic3r/GCode/PreviewData.hpp"
#include "../../libslic3r/Print.hpp"
#include "../../libslic3r/Slicing.hpp"
#include "../../slic3r/GUI/PresetBundle.hpp"
#include "GCode/Analyzer.hpp"
#include <stdio.h>
@ -304,7 +305,7 @@ void GLVolume::render_using_layer_height() const
glUniform1f(z_texture_row_to_normalized_id, (GLfloat)(1.0f / layer_height_texture_height()));
if (z_cursor_id >= 0)
glUniform1f(z_cursor_id, (GLfloat)(bounding_box.max.z * layer_height_texture_data.z_cursor_relative));
glUniform1f(z_cursor_id, (GLfloat)(layer_height_texture_data.print_object->model_object()->bounding_box().max.z * layer_height_texture_data.z_cursor_relative));
if (z_cursor_band_width_id >= 0)
glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width);
@ -326,6 +327,11 @@ void GLVolume::render_using_layer_height() const
glUseProgram(current_program_id);
}
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);
}
void GLVolume::generate_layer_height_texture(PrintObject *print_object, bool force)
{
GLTexture *tex = this->layer_height_texture.get();
@ -381,6 +387,15 @@ std::vector<int> GLVolumeCollection::load_object(
std::vector<int> volumes_idx;
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) {
const ModelVolume *model_volume = model_object->volumes[volume_idx];
int extruder_id = -1;
if (!model_volume->modifier)
{
extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0;
if (extruder_id == 0)
extruder_id = model_object->config.has("extruder") ? model_object->config.option("extruder")->getInt() : 0;
}
for (int instance_idx : instance_idxs) {
const ModelInstance *instance = model_object->instances[instance_idx];
TriangleMesh mesh = model_volume->mesh;
@ -410,8 +425,14 @@ std::vector<int> GLVolumeCollection::load_object(
v.drag_group_id = obj_idx * 1000;
else if (drag_by == "instance")
v.drag_group_id = obj_idx * 1000 + instance_idx;
if (! model_volume->modifier)
if (!model_volume->modifier)
{
v.layer_height_texture = layer_height_texture;
if (extruder_id != -1)
v.extruder_id = extruder_id;
}
v.is_modifier = model_volume->modifier;
}
}
@ -420,12 +441,17 @@ std::vector<int> GLVolumeCollection::load_object(
int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, bool use_VBOs)
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, 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);
auto mesh = make_cube(width, depth, height);
mesh.translate(-width/2.f,-depth/2.f,0.f);
Point origin_of_rotation(0.f,0.f);
mesh.rotate(rotation_angle,&origin_of_rotation);
if (use_VBOs)
v.indexed_vertex_array.load_mesh_full_shading(mesh);
else
@ -438,6 +464,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
v.composite_id = obj_idx * 1000000;
v.select_group_id = obj_idx * 1000000;
v.drag_group_id = obj_idx * 1000;
v.is_wipe_tower = true;
return int(this->volumes.size() - 1);
}
@ -642,6 +669,83 @@ void GLVolumeCollection::update_outside_state(const DynamicPrintConfig* config,
}
}
void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* config)
{
static const float inv_255 = 1.0f / 255.0f;
struct Color
{
std::string text;
unsigned char rgb[3];
Color()
: text("")
{
rgb[0] = 255;
rgb[1] = 255;
rgb[2] = 255;
}
void set(const std::string& text, unsigned char* rgb)
{
this->text = text;
::memcpy((void*)this->rgb, (const void*)rgb, 3 * sizeof(unsigned char));
}
};
if (config == nullptr)
return;
const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("extruder_colour"));
if (extruders_opt == nullptr)
return;
const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("filament_colour"));
if (filamemts_opt == nullptr)
return;
unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size());
if (colors_count == 0)
return;
std::vector<Color> colors(colors_count);
unsigned char rgb[3];
for (unsigned int i = 0; i < colors_count; ++i)
{
const std::string& txt_color = config->opt_string("extruder_colour", i);
if (PresetBundle::parse_color(txt_color, rgb))
{
colors[i].set(txt_color, rgb);
}
else
{
const std::string& txt_color = config->opt_string("filament_colour", i);
if (PresetBundle::parse_color(txt_color, rgb))
colors[i].set(txt_color, rgb);
}
}
for (GLVolume* volume : volumes)
{
if ((volume == nullptr) || volume->is_modifier || volume->is_wipe_tower)
continue;
int extruder_id = volume->extruder_id - 1;
if ((extruder_id < 0) || ((unsigned int)colors.size() <= extruder_id))
extruder_id = 0;
const Color& color = colors[extruder_id];
if (!color.text.empty())
{
for (int i = 0; i < 3; ++i)
{
volume->color[i] = (float)color.rgb[i] * inv_255;
}
}
}
}
std::vector<double> GLVolumeCollection::get_current_print_zs() const
{
// Collect layer top positions of all volumes.
@ -2594,8 +2698,10 @@ void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, boo
coordf_t max_z = print.objects[0]->model_object()->get_model()->bounding_box().max.z;
const PrintConfig& config = print.config;
unsigned int extruders_count = config.nozzle_diameter.size();
if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects)
volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, config.wipe_tower_per_color_wipe * (extruders_count - 1), max_z, use_VBOs);
if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
const float width_per_extruder = 15.f; // a simple workaround after wipe_tower_per_color_wipe got obsolete
volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, use_VBOs);
}
}
}
} // namespace Slic3r

View file

@ -246,12 +246,15 @@ public:
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))
{
@ -271,7 +274,7 @@ public:
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);
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
// Bounding box of this volume, in unscaled coordinates.
BoundingBoxf3 bounding_box;
@ -287,6 +290,8 @@ public:
int select_group_id;
// An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance.
int drag_group_id;
// An ID containing the extruder ID (used to select color).
int extruder_id;
// Is this object selected?
bool selected;
// Whether or not this volume is active for rendering
@ -299,6 +304,10 @@ public:
bool is_outside;
// Boolean: Is mouse over this object?
bool hover;
// Wheter or not this volume has been generated from a modifier
bool is_modifier;
// Wheter or not this volume has been generated from the wipe tower
bool is_wipe_tower;
// Interleaved triangles & normals with indexed triangles & quads.
GLIndexedVertexArray indexed_vertex_array;
@ -352,10 +361,7 @@ public:
return (layer_height_texture.get() == nullptr) ? 0 :
(void*)(layer_height_texture->data.data() + layer_height_texture->width * layer_height_texture->height * 4);
}
double layer_height_texture_z_to_row_id() const {
return (this->layer_height_texture.get() == nullptr) ? 0. :
double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * bounding_box.max.z);
}
double layer_height_texture_z_to_row_id() const;
void generate_layer_height_texture(PrintObject *print_object, bool force);
void set_layer_height_texture_data(unsigned int texture_id, unsigned int shader_id, PrintObject* print_object, float z_cursor_relative, float edit_band_width)
@ -392,7 +398,7 @@ public:
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);
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
// Render the volumes by OpenGL.
void render_VBOs() const;
@ -417,6 +423,7 @@ public:
}
void update_outside_state(const DynamicPrintConfig* config, bool all_inside);
void update_colors_by_extruder(const DynamicPrintConfig* config);
// Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection
std::vector<double> get_current_print_zs() const;

View file

@ -34,7 +34,7 @@ void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt)
void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
{
// on_change(nullptr);
// on_change(nullptr);
auto box = new wxStaticBox(this, wxID_ANY, _(L("Shape")));
auto sbsizer = new wxStaticBoxSizer(box, wxVERTICAL);

View file

@ -1,5 +1,14 @@
#include "BitmapCache.hpp"
#if ! defined(WIN32) && ! defined(__APPLE__)
#define BROKEN_ALPHA
#endif
#ifdef BROKEN_ALPHA
#include <wx/mstream.h>
#include <wx/rawbmp.h>
#endif /* BROKEN_ALPHA */
namespace Slic3r { namespace GUI {
void BitmapCache::clear()
@ -8,19 +17,31 @@ void BitmapCache::clear()
delete bitmap.second;
}
static wxBitmap wxImage_to_wxBitmap_with_alpha(wxImage &&image)
{
#ifdef BROKEN_ALPHA
wxMemoryOutputStream stream;
image.SaveFile(stream, wxBITMAP_TYPE_PNG);
wxStreamBuffer *buf = stream.GetOutputStreamBuffer();
return wxBitmap::NewFromPNGData(buf->GetBufferStart(), buf->GetBufferSize());
#else
return wxBitmap(std::move(image));
#endif
}
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height)
{
wxBitmap *bitmap = nullptr;
auto it = m_map.find(bitmap_key);
if (it == m_map.end()) {
bitmap = new wxBitmap(width, height);
m_map[bitmap_key] = bitmap;
} else {
bitmap = it->second;
if (bitmap->GetWidth() != width || bitmap->GetHeight() != height)
bitmap->Create(width, height);
}
#if defined(__APPLE__) || defined(_MSC_VER)
wxBitmap *bitmap = nullptr;
auto it = m_map.find(bitmap_key);
if (it == m_map.end()) {
bitmap = new wxBitmap(width, height);
m_map[bitmap_key] = bitmap;
} else {
bitmap = it->second;
if (bitmap->GetWidth() != width || bitmap->GetHeight() != height)
bitmap->Create(width, height);
}
#ifndef BROKEN_ALPHA
bitmap->UseAlpha();
#endif
return bitmap;
@ -28,77 +49,108 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp)
{
wxBitmap *bitmap = this->insert(bitmap_key, bmp.GetWidth(), bmp.GetHeight());
wxMemoryDC memDC;
memDC.SelectObject(*bitmap);
memDC.SetBackground(*wxTRANSPARENT_BRUSH);
memDC.Clear();
memDC.DrawBitmap(bmp, 0, 0, true);
memDC.SelectObject(wxNullBitmap);
return bitmap;
wxBitmap *bitmap = nullptr;
auto it = m_map.find(bitmap_key);
if (it == m_map.end()) {
bitmap = new wxBitmap(bmp);
m_map[bitmap_key] = bitmap;
} else {
bitmap = it->second;
*bitmap = bmp;
}
return bitmap;
}
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2)
{
wxBitmap *bitmap = this->insert(bitmap_key, bmp.GetWidth() + bmp2.GetWidth(), std::max(bmp.GetHeight(), bmp2.GetHeight()));
wxMemoryDC memDC;
memDC.SelectObject(*bitmap);
memDC.SetBackground(*wxTRANSPARENT_BRUSH);
memDC.Clear();
if (bmp.GetWidth() > 0)
memDC.DrawBitmap(bmp, 0, 0, true);
if (bmp2.GetWidth() > 0)
memDC.DrawBitmap(bmp2, bmp.GetWidth(), 0, true);
memDC.SelectObject(wxNullBitmap);
return bitmap;
// Copying the wxBitmaps is cheap as the bitmap's content is reference counted.
const wxBitmap bmps[2] = { bmp, bmp2 };
return this->insert(bitmap_key, bmps, bmps + 2);
}
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3)
{
wxBitmap *bitmap = this->insert(bitmap_key, bmp.GetWidth() + bmp2.GetWidth() + bmp3.GetWidth(), std::max(std::max(bmp.GetHeight(), bmp2.GetHeight()), bmp3.GetHeight()));
wxMemoryDC memDC;
memDC.SelectObject(*bitmap);
memDC.SetBackground(*wxTRANSPARENT_BRUSH);
memDC.Clear();
if (bmp.GetWidth() > 0)
memDC.DrawBitmap(bmp, 0, 0, true);
if (bmp2.GetWidth() > 0)
memDC.DrawBitmap(bmp2, bmp.GetWidth(), 0, true);
if (bmp3.GetWidth() > 0)
memDC.DrawBitmap(bmp3, bmp.GetWidth() + bmp2.GetWidth(), 0, true);
memDC.SelectObject(wxNullBitmap);
return bitmap;
// Copying the wxBitmaps is cheap as the bitmap's content is reference counted.
const wxBitmap bmps[3] = { bmp, bmp2, bmp3 };
return this->insert(bitmap_key, bmps, bmps + 3);
}
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, std::vector<wxBitmap> &bmps)
wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *begin, const wxBitmap *end)
{
size_t width = 0;
size_t height = 0;
for (wxBitmap &bmp : bmps) {
width += bmp.GetWidth();
height = std::max<size_t>(height, bmp.GetHeight());
}
wxBitmap *bitmap = this->insert(bitmap_key, width, height);
size_t width = 0;
size_t height = 0;
for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
width += bmp->GetWidth();
height = std::max<size_t>(height, bmp->GetHeight());
}
#ifdef BROKEN_ALPHA
wxImage image(width, height);
image.InitAlpha();
// Fill in with a white color.
memset(image.GetData(), 0x0ff, width * height * 3);
// Fill in with full transparency.
memset(image.GetAlpha(), 0, width * height);
size_t x = 0;
for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
if (bmp->GetWidth() > 0) {
if (bmp->GetDepth() == 32) {
wxAlphaPixelData data(*const_cast<wxBitmap*>(bmp));
data.UseAlpha();
if (data) {
for (int r = 0; r < bmp->GetHeight(); ++ r) {
wxAlphaPixelData::Iterator src(data);
src.Offset(data, 0, r);
unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3;
unsigned char *dst_alpha = image.GetAlpha() + x + r * width;
for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) {
*dst_pixels ++ = src.Red();
*dst_pixels ++ = src.Green();
*dst_pixels ++ = src.Blue();
*dst_alpha ++ = src.Alpha();
}
}
}
} else if (bmp->GetDepth() == 24) {
wxNativePixelData data(*const_cast<wxBitmap*>(bmp));
if (data) {
for (int r = 0; r < bmp->GetHeight(); ++ r) {
wxNativePixelData::Iterator src(data);
src.Offset(data, 0, r);
unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3;
unsigned char *dst_alpha = image.GetAlpha() + x + r * width;
for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) {
*dst_pixels ++ = src.Red();
*dst_pixels ++ = src.Green();
*dst_pixels ++ = src.Blue();
*dst_alpha ++ = wxALPHA_OPAQUE;
}
}
}
}
}
x += bmp->GetWidth();
}
return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image)));
#else
wxBitmap *bitmap = this->insert(bitmap_key, width, height);
wxMemoryDC memDC;
memDC.SelectObject(*bitmap);
memDC.SetBackground(*wxTRANSPARENT_BRUSH);
memDC.Clear();
size_t x = 0;
for (wxBitmap &bmp : bmps) {
if (bmp.GetWidth() > 0)
memDC.DrawBitmap(bmp, x, 0, true);
x += bmp.GetWidth();
}
for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) {
if (bmp->GetWidth() > 0)
memDC.DrawBitmap(*bmp, x, 0, true);
x += bmp->GetWidth();
}
memDC.SelectObject(wxNullBitmap);
return bitmap;
return bitmap;
#endif
}
wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency)
@ -113,7 +165,7 @@ wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsi
*imgdata ++ = b;
*imgalpha ++ = transparency;
}
return wxBitmap(std::move(image));
return wxImage_to_wxBitmap_with_alpha(std::move(image));
}
} // namespace GUI

View file

@ -27,7 +27,8 @@ public:
wxBitmap* insert(const std::string &name, const wxBitmap &bmp);
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2);
wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3);
wxBitmap* insert(const std::string &name, std::vector<wxBitmap> &bmps);
wxBitmap* insert(const std::string &name, const std::vector<wxBitmap> &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); }
wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end);
static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency);
static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); }

View file

@ -75,13 +75,13 @@ namespace Slic3r { namespace GUI {
return tooltip_text;
}
bool Field::is_matched(std::string string, std::string pattern)
bool Field::is_matched(const std::string& string, const std::string& pattern)
{
std::regex regex_pattern(pattern, std::regex_constants::icase); // use ::icase to make the matching case insensitive like /i in perl
return std::regex_match(string, regex_pattern);
}
boost::any Field::get_value_by_opt_type(wxString str)
boost::any Field::get_value_by_opt_type(wxString& str)
{
boost::any ret_val;
switch (m_opt.type){
@ -173,8 +173,7 @@ namespace Slic3r { namespace GUI {
temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e)
{
// on_kill_focus(e);
e.Skip();
e.Skip();// on_kill_focus(e);
temp->GetToolTip()->Enable(true);
}), temp->GetId());
@ -378,7 +377,7 @@ void Choice::set_selection()
}
}
void Choice::set_value(const std::string value, bool change_event) //! Redundant?
void Choice::set_value(const std::string& value, bool change_event) //! Redundant?
{
m_disable_change_event = !change_event;
@ -397,7 +396,7 @@ void Choice::set_value(const std::string value, bool change_event) //! Redundan
m_disable_change_event = false;
}
void Choice::set_value(boost::any value, bool change_event)
void Choice::set_value(const boost::any& value, bool change_event)
{
m_disable_change_event = !change_event;
@ -436,7 +435,7 @@ void Choice::set_value(boost::any value, bool change_event)
}
//! it's needed for _update_serial_ports()
void Choice::set_values(const std::vector<std::string> values)
void Choice::set_values(const std::vector<std::string>& values)
{
if (values.empty())
return;
@ -555,7 +554,7 @@ void PointCtrl::BUILD()
y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y));
}
void PointCtrl::set_value(const Pointf value, bool change_event)
void PointCtrl::set_value(const Pointf& value, bool change_event)
{
m_disable_change_event = !change_event;
@ -567,10 +566,10 @@ void PointCtrl::set_value(const Pointf value, bool change_event)
m_disable_change_event = false;
}
void PointCtrl::set_value(boost::any value, bool change_event)
void PointCtrl::set_value(const boost::any& value, bool change_event)
{
Pointf pt;
Pointf *ptf = boost::any_cast<Pointf>(&value);
const Pointf *ptf = boost::any_cast<Pointf>(&value);
if (!ptf)
{
ConfigOptionPoints* pts = boost::any_cast<ConfigOptionPoints*>(value);
@ -578,21 +577,6 @@ void PointCtrl::set_value(boost::any value, bool change_event)
}
else
pt = *ptf;
// try
// {
// pt = boost::any_cast<ConfigOptionPoints*>(value)->values.at(0);
// }
// catch (const std::exception &e)
// {
// try{
// pt = boost::any_cast<Pointf>(value);
// }
// catch (const std::exception &e)
// {
// std::cerr << "Error! Can't cast PointCtrl value" << m_opt_id << "\n";
// return;
// }
// }
set_value(pt, change_event);
}

View file

@ -31,8 +31,8 @@ namespace Slic3r { namespace GUI {
class Field;
using t_field = std::unique_ptr<Field>;
using t_kill_focus = std::function<void()>;
using t_change = std::function<void(t_config_option_key, boost::any)>;
using t_back_to_init = std::function<void(std::string)>;
using t_change = std::function<void(t_config_option_key, const boost::any&)>;
using t_back_to_init = std::function<void(const std::string&)>;
wxString double_to_string(double const value);
@ -81,7 +81,7 @@ public:
/// Sets a value for this control.
/// subclasses should overload with a specific version
/// Postcondition: Method does not fire the on_change event.
virtual void set_value(boost::any value, bool change_event) = 0;
virtual void set_value(const boost::any& value, bool change_event) = 0;
/// Gets a boost::any representing this control.
/// subclasses should overload with a specific version
@ -100,7 +100,7 @@ public:
virtual wxString get_tooltip_text(const wxString& default_string);
// set icon to "UndoToSystemValue" button according to an inheritance of preset
void set_nonsys_btn_icon(const std::string& icon);
void set_nonsys_btn_icon(const std::string& icon);
Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id) {};
Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id) {};
@ -109,8 +109,8 @@ public:
virtual wxSizer* getSizer() { return nullptr; }
virtual wxWindow* getWindow() { return nullptr; }
bool is_matched(std::string string, std::string pattern);
boost::any get_value_by_opt_type(wxString str);
bool is_matched(const std::string& string, const std::string& pattern);
boost::any get_value_by_opt_type(wxString& str);
/// Factory method for generating new derived classes.
template<class T>
@ -137,16 +137,17 @@ class TextCtrl : public Field {
public:
TextCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
~TextCtrl() {}
void BUILD();
wxWindow* window {nullptr};
virtual void set_value(std::string value, bool change_event = false) {
virtual void set_value(const std::string& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value));
m_disable_change_event = false;
}
virtual void set_value(boost::any value, bool change_event = false) {
virtual void set_value(const boost::any& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
m_disable_change_event = false;
@ -164,6 +165,7 @@ class CheckBox : public Field {
public:
CheckBox(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
CheckBox(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
~CheckBox() {}
wxWindow* window{ nullptr };
void BUILD() override;
@ -173,7 +175,7 @@ public:
dynamic_cast<wxCheckBox*>(window)->SetValue(value);
m_disable_change_event = false;
}
void set_value(boost::any value, bool change_event = false) {
void set_value(const boost::any& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
m_disable_change_event = false;
@ -190,21 +192,22 @@ class SpinCtrl : public Field {
public:
SpinCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id), tmp_value(-9999) {}
SpinCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id), tmp_value(-9999) {}
~SpinCtrl() {}
int tmp_value;
wxWindow* window{ nullptr };
void BUILD() override;
void set_value(const std::string value, bool change_event = false) {
void set_value(const std::string& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxSpinCtrl*>(window)->SetValue(value);
m_disable_change_event = false;
}
void set_value(boost::any value, bool change_event = false) {
void set_value(const boost::any& value, bool change_event = false) {
m_disable_change_event = !change_event;
tmp_value = boost::any_cast<int>(value);
dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value/*boost::any_cast<int>(value)*/);
dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value);
m_disable_change_event = false;
}
boost::any get_value() override {
@ -221,14 +224,15 @@ class Choice : public Field {
public:
Choice(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
Choice(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
~Choice() {}
wxWindow* window{ nullptr };
void BUILD() override;
void set_selection();
void set_value(const std::string value, bool change_event = false);
void set_value(boost::any value, bool change_event = false);
void set_values(const std::vector<std::string> values);
void set_value(const std::string& value, bool change_event = false);
void set_value(const boost::any& value, bool change_event = false);
void set_values(const std::vector<std::string> &values);
boost::any get_value() override;
void enable() override { dynamic_cast<wxComboBox*>(window)->Enable(); };
@ -241,16 +245,17 @@ class ColourPicker : public Field {
public:
ColourPicker(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
ColourPicker(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
~ColourPicker() {}
wxWindow* window{ nullptr };
void BUILD() override;
void set_value(const std::string value, bool change_event = false) {
void set_value(const std::string& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value);
m_disable_change_event = false;
}
void set_value(boost::any value, bool change_event = false) {
void set_value(const boost::any& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(boost::any_cast<wxString>(value));
m_disable_change_event = false;
@ -268,23 +273,24 @@ class PointCtrl : public Field {
public:
PointCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
PointCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
~PointCtrl() {}
wxSizer* sizer{ nullptr };
wxTextCtrl* x_textctrl;
wxTextCtrl* y_textctrl;
wxTextCtrl* x_textctrl{ nullptr };
wxTextCtrl* y_textctrl{ nullptr };
void BUILD() override;
void set_value(const Pointf value, bool change_event = false);
void set_value(boost::any value, bool change_event = false);
void set_value(const Pointf& value, bool change_event = false);
void set_value(const boost::any& value, bool change_event = false);
boost::any get_value() override;
void enable() override {
x_textctrl->Enable();
y_textctrl->Enable(); };
y_textctrl->Enable(); }
void disable() override{
x_textctrl->Disable();
y_textctrl->Disable(); };
y_textctrl->Disable(); }
wxSizer* getSizer() override { return sizer; }
};

View file

@ -1,6 +1,8 @@
#include "GUI.hpp"
#include "WipeTowerDialog.hpp"
#include <assert.h>
#include <cmath>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp>
@ -38,6 +40,7 @@
#include <wx/combo.h>
#include <wx/window.h>
#include <wx/msgdlg.h>
#include <wx/settings.h>
#include "wxExtensions.hpp"
@ -182,6 +185,8 @@ wxNotebook *g_wxTabPanel = nullptr;
AppConfig *g_AppConfig = nullptr;
PresetBundle *g_PresetBundle= nullptr;
PresetUpdater *g_PresetUpdater = nullptr;
wxColour g_color_label_modified;
wxColour g_color_label_sys;
std::vector<Tab *> g_tabs_list;
@ -189,10 +194,24 @@ wxLocale* g_wxLocale;
std::shared_ptr<ConfigOptionsGroup> m_optgroup;
double m_brim_width = 0.0;
wxButton* g_wiping_dialog_button = nullptr;
static void init_label_colours()
{
auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
if (luma >= 128) {
g_color_label_modified = wxColour(253, 88, 0);
g_color_label_sys = wxColour(26, 132, 57);
} else {
g_color_label_modified = wxColour(253, 111, 40);
g_color_label_sys = wxColour(115, 220, 103);
}
}
void set_wxapp(wxApp *app)
{
g_wxApp = app;
init_label_colours();
}
void set_main_frame(wxFrame *main_frame)
@ -531,7 +550,7 @@ TabIface* get_preset_tab_iface(char *name)
}
// opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element)
void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, boost::any value, int opt_index /*= 0*/)
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/)
{
try{
switch (config.def()->get(opt_key)->type){
@ -567,11 +586,18 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b
config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value)));
break;
case coStrings:{
if (opt_key.compare("compatible_printers") == 0 ||
config.def()->get(opt_key)->gui_flags.compare("serialized") == 0){
config.option<ConfigOptionStrings>(opt_key)->values.resize(0);
std::vector<std::string> values = boost::any_cast<std::vector<std::string>>(value);
if (values.size() == 1 && values[0] == "")
if (opt_key.compare("compatible_printers") == 0) {
config.option<ConfigOptionStrings>(opt_key)->values =
boost::any_cast<std::vector<std::string>>(value);
}
else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0){
std::string str = boost::any_cast<std::string>(value);
if (str.back() == ';') str.pop_back();
// Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values.
// Currently used for the post_process config value only.
std::vector<std::string> values;
boost::split(values, str, boost::is_any_of(";"));
if (values.size() == 1 && values[0] == "")
break;
config.option<ConfigOptionStrings>(opt_key)->values = values;
}
@ -638,17 +664,17 @@ void add_created_tab(Tab* panel)
g_wxTabPanel->AddPage(panel, panel->title());
}
void show_error(wxWindow* parent, wxString message){
void show_error(wxWindow* parent, const wxString& message){
auto msg_wingow = new wxMessageDialog(parent, message, _(L("Error")), wxOK | wxICON_ERROR);
msg_wingow->ShowModal();
}
void show_info(wxWindow* parent, wxString message, wxString title){
void show_info(wxWindow* parent, const wxString& message, const wxString& title){
auto msg_wingow = new wxMessageDialog(parent, message, title.empty() ? _(L("Notice")) : title, wxOK | wxICON_INFORMATION);
msg_wingow->ShowModal();
}
void warning_catcher(wxWindow* parent, wxString message){
void warning_catcher(wxWindow* parent, const wxString& message){
if (message == _(L("GLUquadricObjPtr | Attempt to free unreferenced scalar")) )
return;
auto msg = new wxMessageDialog(parent, message, _(L("Warning")), wxOK | wxICON_WARNING);
@ -659,12 +685,25 @@ wxApp* get_app(){
return g_wxApp;
}
wxColour* get_modified_label_clr(){
return new wxColour(253, 88, 0);
const wxColour& get_modified_label_clr() {
return g_color_label_modified;
}
wxColour* get_sys_label_clr(){
return new wxColour(26, 132, 57);
const wxColour& get_sys_label_clr() {
return g_color_label_sys;
}
unsigned get_colour_approx_luma(const wxColour &colour)
{
double r = colour.Red();
double g = colour.Green();
double b = colour.Blue();
return std::round(std::sqrt(
r * r * .241 +
g * g * .691 +
b * b * .068
));
}
void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value)
@ -829,6 +868,33 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
option = Option(def, "brim");
m_optgroup->append_single_option_line(option);
Line line = { _(L("")), "" };
line.widget = [config](wxWindow* parent){
g_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + "\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(g_wiping_dialog_button);
g_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e)
{
auto &config = g_PresetBundle->project_config;
std::vector<double> init_matrix = (config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values;
std::vector<double> init_extruders = (config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values;
WipingDialog dlg(parent,std::vector<float>(init_matrix.begin(),init_matrix.end()),std::vector<float>(init_extruders.begin(),init_extruders.end()));
if (dlg.ShowModal() == wxID_OK) {
std::vector<float> matrix = dlg.get_matrix();
std::vector<float> extruders = dlg.get_extruders();
(config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(),matrix.end());
(config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(),extruders.end());
}
}));
return sizer;
};
m_optgroup->append_line(line);
sizer->Add(m_optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxBottom, 1);
}
@ -837,6 +903,12 @@ ConfigOptionsGroup* get_optgroup()
return m_optgroup.get();
}
wxButton* get_wiping_dialog_button()
{
return g_wiping_dialog_button;
}
wxWindow* export_option_creator(wxWindow* parent)
{
wxPanel* panel = new wxPanel(parent, -1);
@ -880,6 +952,7 @@ int get_export_option(wxFileDialog* dlg)
}
return 0;
}
void about()

View file

@ -18,6 +18,7 @@ class wxArrayLong;
class wxColour;
class wxBoxSizer;
class wxFlexGridSizer;
class wxButton;
class wxFileDialog;
namespace Slic3r {
@ -81,8 +82,10 @@ void set_preset_updater(PresetUpdater *updater);
AppConfig* get_app_config();
wxApp* get_app();
wxColour* get_modified_label_clr();
wxColour* get_sys_label_clr();
const wxColour& get_modified_label_clr();
const wxColour& get_sys_label_clr();
unsigned get_colour_approx_luma(const wxColour &colour);
extern void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
@ -106,11 +109,11 @@ TabIface* get_preset_tab_iface(char *name);
// add it at the end of the tab panel.
void add_created_tab(Tab* panel);
// Change option value in config
void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, boost::any value, int opt_index = 0);
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0);
void show_error(wxWindow* parent, wxString message);
void show_info(wxWindow* parent, wxString message, wxString title);
void warning_catcher(wxWindow* parent, wxString message);
void show_error(wxWindow* parent, const wxString& message);
void show_info(wxWindow* parent, const wxString& message, const wxString& title);
void warning_catcher(wxWindow* parent, const wxString& message);
// load language saved at application config
bool load_language();
@ -143,6 +146,7 @@ wxString from_u8(const std::string &str);
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
ConfigOptionsGroup* get_optgroup();
wxButton* get_wiping_dialog_button();
void add_export_option(wxFileDialog* dlg, const std::string& format);
int get_export_option(wxFileDialog* dlg);

View file

@ -97,7 +97,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
return field;
}
void OptionsGroup::append_line(const Line& line) {
void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* = nullptr*/) {
//! if (line.sizer != nullptr || (line.widget != nullptr && line.full_width > 0)){
if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width){
if (line.sizer != nullptr) {
@ -150,6 +150,7 @@ void OptionsGroup::append_line(const Line& line) {
if (line.widget != nullptr) {
auto wgt = line.widget(parent());
grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, wxOSX ? 0 : 5);
if (colored_Label != nullptr) *colored_Label = label;
return;
}
@ -227,12 +228,12 @@ Line OptionsGroup::create_single_option_line(const Option& option) const {
return retval;
}
void OptionsGroup::on_change_OG(t_config_option_key id, /*config_value*/boost::any value) {
void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value) {
if (m_on_change != nullptr)
m_on_change(id, value);
m_on_change(opt_id, value);
}
Option ConfigOptionsGroup::get_option(const std::string opt_key, int opt_index /*= -1*/)
Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index /*= -1*/)
{
if (!m_config->has(opt_key)) {
std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.";
@ -245,7 +246,7 @@ Option ConfigOptionsGroup::get_option(const std::string opt_key, int opt_index /
return Option(*m_config->def()->get(opt_key), opt_id);
}
void ConfigOptionsGroup::on_change_OG(t_config_option_key opt_id, boost::any value)
void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value)
{
if (!m_opt_map.empty())
{
@ -268,16 +269,7 @@ void ConfigOptionsGroup::on_change_OG(t_config_option_key opt_id, boost::any val
if (opt_index != -1){
// die "Can't set serialized option indexed value" ;
}
// # Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values.
// # Currently used for the post_process config value only.
// my @values = split / ; / , $field_value;
// $self->config->set($opt_key, \@values);
std::string str = boost::any_cast<std::string>(value);
if (str.back() == ';')
str.pop_back();
std::vector<std::string> values;
boost::split(values, str, boost::is_any_of(";"));
change_opt_value(*m_config, opt_key, values);
change_opt_value(*m_config, opt_key, value);
}
else {
if (opt_index == -1) {
@ -297,14 +289,14 @@ void ConfigOptionsGroup::on_change_OG(t_config_option_key opt_id, boost::any val
OptionsGroup::on_change_OG(opt_id, value); //!? Why doing this
}
void ConfigOptionsGroup::back_to_initial_value(const std::string opt_key)
void ConfigOptionsGroup::back_to_initial_value(const std::string& opt_key)
{
if (m_get_initial_config == nullptr)
return;
back_to_config_value(m_get_initial_config(), opt_key);
}
void ConfigOptionsGroup::back_to_sys_value(const std::string opt_key)
void ConfigOptionsGroup::back_to_sys_value(const std::string& opt_key)
{
if (m_get_sys_config == nullptr)
return;
@ -313,7 +305,7 @@ void ConfigOptionsGroup::back_to_sys_value(const std::string opt_key)
back_to_config_value(m_get_sys_config(), opt_key);
}
void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, const std::string opt_key)
void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key)
{
boost::any value;
if (opt_key == "extruders_count"){
@ -348,7 +340,7 @@ void ConfigOptionsGroup::reload_config(){
}
boost::any ConfigOptionsGroup::config_value(std::string opt_key, int opt_index, bool deserialize){
boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_index, bool deserialize){
if (deserialize) {
// Want to edit a vector value(currently only multi - strings) in a single edit box.
@ -365,7 +357,7 @@ boost::any ConfigOptionsGroup::config_value(std::string opt_key, int opt_index,
}
}
boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config, std::string opt_key, int opt_index /*= -1*/)
boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index /*= -1*/)
{
size_t idx = opt_index == -1 ? 0 : opt_index;
@ -405,6 +397,10 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
ret = static_cast<wxString>(config.opt_string(opt_key));
break;
case coStrings:
if (opt_key.compare("compatible_printers") == 0){
ret = config.option<ConfigOptionStrings>(opt_key)->values;
break;
}
if (config.option<ConfigOptionStrings>(opt_key)->values.empty())
ret = text_value;
else if (opt->gui_flags.compare("serialized") == 0){
@ -457,7 +453,7 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
return ret;
}
Field* ConfigOptionsGroup::get_fieldc(t_config_option_key opt_key, int opt_index){
Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int opt_index){
Field* field = get_field(opt_key);
if (field != nullptr)
return field;
@ -471,7 +467,7 @@ Field* ConfigOptionsGroup::get_fieldc(t_config_option_key opt_key, int opt_index
return opt_id.empty() ? nullptr : get_field(opt_id);
}
void ogStaticText::SetText(wxString value)
void ogStaticText::SetText(const wxString& value)
{
SetLabel(value);
Wrap(400);

View file

@ -93,21 +93,21 @@ public:
/// but defining it as const means a lot of const_casts to deal with wx functions.
inline wxWindow* parent() const { return m_parent; }
void append_line(const Line& line);
void append_line(const Line& line, wxStaticText** colored_Label = nullptr);
Line create_single_option_line(const Option& option) const;
void append_single_option_line(const Option& option) { append_line(create_single_option_line(option)); }
// return a non-owning pointer reference
inline Field* get_field(t_config_option_key id) const{
inline Field* get_field(const t_config_option_key& id) const{
if (m_fields.find(id) == m_fields.end()) return nullptr;
return m_fields.at(id).get();
}
bool set_value(t_config_option_key id, boost::any value, bool change_event = false) {
bool set_value(const t_config_option_key& id, const boost::any& value, bool change_event = false) {
if (m_fields.find(id) == m_fields.end()) return false;
m_fields.at(id)->set_value(value, change_event);
return true;
}
boost::any get_value(t_config_option_key id) {
boost::any get_value(const t_config_option_key& id) {
boost::any out;
if (m_fields.find(id) == m_fields.end()) ;
else
@ -118,7 +118,7 @@ public:
inline void enable() { for (auto& field : m_fields) field.second->enable(); }
inline void disable() { for (auto& field : m_fields) field.second->disable(); }
OptionsGroup(wxWindow* _parent, wxString title, bool is_tab_opt=false) :
OptionsGroup(wxWindow* _parent, const wxString& title, bool is_tab_opt=false) :
m_parent(_parent), title(title), m_is_tab_opt(is_tab_opt), staticbox(title!="") {
sizer = (staticbox ? new wxStaticBoxSizer(new wxStaticBox(_parent, wxID_ANY, title), wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
auto num_columns = 1U;
@ -152,14 +152,14 @@ protected:
const t_field& build_field(const Option& opt, wxStaticText* label = nullptr);
virtual void on_kill_focus (){};
virtual void on_change_OG(t_config_option_key opt_id, boost::any value);
virtual void back_to_initial_value(const std::string opt_key){};
virtual void back_to_sys_value(const std::string opt_key){};
virtual void on_change_OG(const t_config_option_key& opt_id, const boost::any& value);
virtual void back_to_initial_value(const std::string& opt_key){}
virtual void back_to_sys_value(const std::string& opt_key){}
};
class ConfigOptionsGroup: public OptionsGroup {
public:
ConfigOptionsGroup(wxWindow* parent, wxString title, DynamicPrintConfig* _config = nullptr, bool is_tab_opt = false) :
ConfigOptionsGroup(wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, bool is_tab_opt = false) :
OptionsGroup(parent, title, is_tab_opt), m_config(_config) {}
/// reference to libslic3r config, non-owning pointer (?).
@ -167,8 +167,8 @@ public:
bool m_full_labels {0};
t_opt_map m_opt_map;
Option get_option(const std::string opt_key, int opt_index = -1);
Line create_single_option_line(const std::string title, int idx = -1) /*const*/{
Option get_option(const std::string& opt_key, int opt_index = -1);
Line create_single_option_line(const std::string& title, int idx = -1) /*const*/{
Option option = get_option(title, idx);
return OptionsGroup::create_single_option_line(option);
}
@ -181,16 +181,16 @@ public:
append_single_option_line(option);
}
void on_change_OG(t_config_option_key opt_id, boost::any value) override;
void back_to_initial_value(const std::string opt_key) override;
void back_to_sys_value(const std::string opt_key) override;
void back_to_config_value(const DynamicPrintConfig& config, const std::string opt_key);
void on_change_OG(const t_config_option_key& opt_id, const boost::any& value) override;
void back_to_initial_value(const std::string& opt_key) override;
void back_to_sys_value(const std::string& opt_key) override;
void back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key);
void on_kill_focus() override{ reload_config();}
void reload_config();
boost::any config_value(std::string opt_key, int opt_index, bool deserialize);
boost::any config_value(const std::string& opt_key, int opt_index, bool deserialize);
// return option value from config
boost::any get_config_value(const DynamicPrintConfig& config, std::string opt_key, int opt_index = -1);
Field* get_fieldc(t_config_option_key opt_key, int opt_index);
boost::any get_config_value(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index = -1);
Field* get_fieldc(const t_config_option_key& opt_key, int opt_index);
};
// Static text shown among the options.
@ -200,7 +200,7 @@ public:
ogStaticText(wxWindow* parent, const char *text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize){}
~ogStaticText(){}
void SetText(wxString value);
void SetText(const wxString& value);
};
}}

View file

@ -287,8 +287,7 @@ 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_per_color_wipe",
"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;
}
@ -296,12 +295,12 @@ const std::vector<std::string>& Preset::print_options()
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", "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_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_cooling_time", "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;
}
@ -314,8 +313,8 @@ const std::vector<std::string>& Preset::printer_options()
"bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
"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", "max_print_height",
"default_print_profile", "inherits",
"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",
};
s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end());
}
@ -505,6 +504,20 @@ const Preset* PresetCollection::get_selected_preset_parent() const
return (preset == nullptr || preset->is_default || preset->is_external) ? nullptr : preset;
}
const Preset* PresetCollection::get_preset_parent(const Preset& child) const
{
auto *inherits = dynamic_cast<const ConfigOptionString*>(child.config.option("inherits"));
if (inherits == nullptr || inherits->value.empty())
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
return nullptr;
const Preset* preset = this->find_preset(inherits->value, false);
return (preset == nullptr/* || preset->is_default */|| preset->is_external) ? nullptr : preset;
}
const std::string& PresetCollection::get_suffix_modified() {
return g_suffix_modified;
}
// Return a preset by its name. If the preset is active, a temporary copy is returned.
// If a preset is not found by its name, null is returned.
Preset* PresetCollection::find_preset(const std::string &name, bool first_visible_if_not_found)
@ -572,16 +585,44 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
// Otherwise fill in the list from scratch.
ui->Freeze();
ui->Clear();
std::map<wxString, bool> nonsys_presets;
wxString selected = "";
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) {
const Preset &preset = this->m_presets[i];
if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected))
continue;
const wxBitmap *bmp = (i == 0 || preset.is_compatible) ? m_bitmap_main_frame : m_bitmap_incompatible;
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);
}
// 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);
if (preset.is_default || preset.is_system){
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);
}
else
{
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), preset.is_compatible);
if (i == m_idx_selected)
selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
}
if (preset.is_default)
ui->Append("------------------------------------", wxNullBitmap);
}
if (!nonsys_presets.empty())
{
ui->Append("------------------------------------", wxNullBitmap);
for (std::map<wxString, bool>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
const wxBitmap *bmp = it->second ? m_bitmap_compatible : m_bitmap_incompatible;
ui->Append(it->first,
(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
if (it->first == selected)
ui->SetSelection(ui->GetCount() - 1);
}
}
ui->Thaw();
}
@ -591,16 +632,44 @@ void PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatibl
return;
ui->Freeze();
ui->Clear();
std::map<wxString, bool> nonsys_presets;
wxString selected = "";
for (size_t i = this->m_presets.front().is_visible ? 0 : 1; i < this->m_presets.size(); ++ i) {
const Preset &preset = this->m_presets[i];
if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected))
continue;
const wxBitmap *bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible;
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);
// 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);
if (preset.is_default || preset.is_system){
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);
}
else
{
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), preset.is_compatible);
if (i == m_idx_selected)
selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
}
if (preset.is_default)
ui->Append("------------------------------------", wxNullBitmap);
}
if (!nonsys_presets.empty())
{
ui->Append("------------------------------------", wxNullBitmap);
for (std::map<wxString, bool>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
const wxBitmap *bmp = it->second ? m_bitmap_compatible : m_bitmap_incompatible;
ui->Append(it->first,
(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
if (it->first == selected)
ui->SetSelection(ui->GetCount() - 1);
}
}
ui->Thaw();
}

View file

@ -228,9 +228,17 @@ public:
// The parent preset may be a system preset or a user preset, which will be
// reflected by the UI.
const Preset* get_selected_preset_parent() const;
// Return the selected preset including the user modifications.
// get parent preset for some child preset
const Preset* get_preset_parent(const Preset& child) const;
// Return the selected preset including the user modifications.
Preset& get_edited_preset() { return m_edited_preset; }
const Preset& get_edited_preset() const { return m_edited_preset; }
// used to update preset_choice from Tab
const std::deque<Preset>& get_presets() { return m_presets; }
int get_idx_selected() { return m_idx_selected; }
const std::string& get_suffix_modified();
// Return a preset possibly with modifications.
const Preset& default_preset() const { return m_presets.front(); }
// Return a preset by an index. If the preset is active, a temporary copy is returned.

View file

@ -34,6 +34,11 @@
namespace Slic3r {
static std::vector<std::string> s_project_options {
"wiping_volumes_extruders",
"wiping_volumes_matrix"
};
PresetBundle::PresetBundle() :
prints(Preset::TYPE_PRINT, Preset::print_options()),
filaments(Preset::TYPE_FILAMENT, Preset::filament_options()),
@ -69,6 +74,8 @@ PresetBundle::PresetBundle() :
this->filaments.load_bitmap_default("spool.png");
this->printers .load_bitmap_default("printer_empty.png");
this->load_compatible_bitmaps();
this->project_config.apply_only(FullPrintConfig::defaults(), s_project_options);
}
PresetBundle::~PresetBundle()
@ -274,8 +281,8 @@ bool PresetBundle::load_compatible_bitmaps()
{
const std::string path_bitmap_compatible = "flag-green-icon.png";
const std::string path_bitmap_incompatible = "flag-red-icon.png";
const std::string path_bitmap_lock = "lock.png";
const std::string path_bitmap_lock_open = "lock_open.png";
const std::string path_bitmap_lock = "sys_lock.png";//"lock.png";
const std::string path_bitmap_lock_open = "sys_unlock.png";//"lock_open.png";
bool loaded_compatible = m_bitmapCompatible ->LoadFile(
wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG);
bool loaded_incompatible = m_bitmapIncompatible->LoadFile(
@ -313,6 +320,7 @@ DynamicPrintConfig PresetBundle::full_config() const
out.apply(FullPrintConfig());
out.apply(this->prints.get_edited_preset().config);
out.apply(this->printers.get_edited_preset().config);
out.apply(this->project_config);
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(out.option("nozzle_diameter"));
size_t num_extruders = nozzle_diameter->values.size();
@ -502,6 +510,9 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
}
}
// 4) Load the project config values (the per extruder wipe matrix etc).
this->project_config.apply_only(config, s_project_options);
this->update_compatible_with_printer(false);
}
@ -868,6 +879,34 @@ void PresetBundle::update_multi_material_filament_presets()
// Append the rest of filament presets.
// if (this->filament_presets.size() < num_extruders)
this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back());
// Now verify if wiping_volumes_matrix has proper size (it is used to deduce number of extruders in wipe tower generator):
std::vector<double> old_matrix = this->project_config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values;
size_t old_number_of_extruders = int(sqrt(old_matrix.size())+EPSILON);
if (num_extruders != old_number_of_extruders) {
// First verify if purging volumes presets for each extruder matches number of extruders
std::vector<double>& extruders = this->project_config.option<ConfigOptionFloats>("wiping_volumes_extruders")->values;
while (extruders.size() < 2*num_extruders) {
extruders.push_back(extruders.size()>1 ? extruders[0] : 50.); // copy the values from the first extruder
extruders.push_back(extruders.size()>1 ? extruders[1] : 50.);
}
while (extruders.size() > 2*num_extruders) {
extruders.pop_back();
extruders.pop_back();
}
std::vector<double> new_matrix;
for (unsigned int i=0;i<num_extruders;++i)
for (unsigned int j=0;j<num_extruders;++j) {
// append the value for this pair from the old matrix (if it's there):
if (i<old_number_of_extruders && j<old_number_of_extruders)
new_matrix.push_back(old_matrix[i*old_number_of_extruders + j]);
else
new_matrix.push_back( i==j ? 0. : extruders[2*i]+extruders[2*j+1]); // so it matches new extruder volumes
}
this->project_config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values = new_matrix;
}
}
void PresetBundle::update_compatible_with_printer(bool select_other_if_incompatible)
@ -967,7 +1006,7 @@ static inline int hex_digit_to_int(const char c)
(c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
}
static inline bool parse_color(const std::string &scolor, unsigned char *rgb_out)
bool PresetBundle::parse_color(const std::string &scolor, unsigned char *rgb_out)
{
rgb_out[0] = rgb_out[1] = rgb_out[2] = 0;
if (scolor.size() != 7 || scolor.front() != '#')
@ -1002,6 +1041,8 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
// and draw a red flag in front of the selected preset.
bool wide_icons = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr;
assert(selected_preset != nullptr);
std::map<wxString, wxBitmap> nonsys_presets;
wxString selected_str = "";
for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++ i) {
const Preset &preset = this->filaments.preset(i);
bool selected = this->filament_presets[idx_extruder] == preset.name;
@ -1039,10 +1080,36 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
(preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16));
bitmap = m_bitmapCache->insert(bitmap_key, bmps);
}
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);
// 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);
if (preset.is_default || preset.is_system){
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);
}
else
{
nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()),
(bitmap == 0) ? wxNullBitmap : *bitmap);
if (selected)
selected_str = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str());
}
if (preset.is_default)
ui->Append("------------------------------------", wxNullBitmap);
}
if (!nonsys_presets.empty())
{
ui->Append("------------------------------------", wxNullBitmap);
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);
}
}
ui->Thaw();
}

View file

@ -45,6 +45,11 @@ public:
// extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size()
std::vector<std::string> filament_presets;
// The project configuration values are kept separated from the print/filament/printer preset,
// they are being serialized / deserialized from / to the .amf, .3mf, .config, .gcode,
// and they are being used by slicing core.
DynamicPrintConfig project_config;
// There will be an entry for each system profile loaded,
// and the system profiles will point to the VendorProfile instances owned by PresetBundle::vendors.
std::set<VendorProfile> vendors;
@ -115,6 +120,8 @@ public:
// preset if the current print or filament preset is not compatible.
void update_compatible_with_printer(bool select_other_if_incompatible);
static bool parse_color(const std::string &scolor, unsigned char *rgb_out);
private:
std::string load_system_presets();

View file

@ -0,0 +1,284 @@
#include <algorithm>
#include <wx/dcbuffer.h>
#include "RammingChart.hpp"
//! macro used to mark string used at localization,
//! return same string
#define L(s) s
wxDEFINE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent);
void Chart::draw() {
wxAutoBufferedPaintDC dc(this); // unbuffered DC caused flickering on win
dc.SetBrush(GetBackgroundColour());
dc.SetPen(GetBackgroundColour());
dc.DrawRectangle(GetClientRect()); // otherwise the background would end up black on windows
dc.SetPen(*wxBLACK_PEN);
dc.SetBrush(*wxWHITE_BRUSH);
dc.DrawRectangle(m_rect);
if (visible_area.m_width < 0.499) {
dc.DrawText("NO RAMMING AT ALL",wxPoint(m_rect.GetLeft()+m_rect.GetWidth()/2-50,m_rect.GetBottom()-m_rect.GetHeight()/2));
return;
}
if (!m_line_to_draw.empty()) {
for (unsigned int i=0;i<m_line_to_draw.size()-2;++i) {
int color = 510*((m_rect.GetBottom()-(m_line_to_draw)[i])/double(m_rect.GetHeight()));
dc.SetPen( wxPen( wxColor(std::min(255,color),255-std::max(color-255,0),0), 1 ) );
dc.DrawLine(m_rect.GetLeft()+1+i,(m_line_to_draw)[i],m_rect.GetLeft()+1+i,m_rect.GetBottom());
}
dc.SetPen( wxPen( wxColor(0,0,0), 1 ) );
for (unsigned int i=0;i<m_line_to_draw.size()-2;++i) {
if (splines)
dc.DrawLine(m_rect.GetLeft()+i,(m_line_to_draw)[i],m_rect.GetLeft()+i+1,(m_line_to_draw)[i+1]);
else {
dc.DrawLine(m_rect.GetLeft()+i,(m_line_to_draw)[i],m_rect.GetLeft()+i+1,(m_line_to_draw)[i]);
dc.DrawLine(m_rect.GetLeft()+i+1,(m_line_to_draw)[i],m_rect.GetLeft()+i+1,(m_line_to_draw)[i+1]);
}
}
}
// draw draggable buttons
dc.SetBrush(*wxBLUE_BRUSH);
dc.SetPen( wxPen( wxColor(0,0,0), 1 ) );
for (auto& button : m_buttons)
//dc.DrawRectangle(math_to_screen(button.get_pos())-wxPoint(side/2.,side/2.), wxSize(side,side));
dc.DrawCircle(math_to_screen(button.get_pos()),side/2.);
//dc.DrawRectangle(math_to_screen(button.get_pos()-wxPoint2DDouble(0.125,0))-wxPoint(0,5),wxSize(50,10));
// draw x-axis:
float last_mark = -10000;
for (float math_x=int(visible_area.m_x*10)/10 ; math_x < (visible_area.m_x+visible_area.m_width) ; math_x+=0.1) {
int x = math_to_screen(wxPoint2DDouble(math_x,visible_area.m_y)).x;
int y = m_rect.GetBottom();
if (x-last_mark < 50) continue;
dc.DrawLine(x,y+3,x,y-3);
dc.DrawText(wxString().Format(wxT("%.1f"), math_x),wxPoint(x-10,y+7));
last_mark = x;
}
// draw y-axis:
last_mark=10000;
for (int math_y=visible_area.m_y ; math_y < (visible_area.m_y+visible_area.m_height) ; math_y+=1) {
int y = math_to_screen(wxPoint2DDouble(visible_area.m_x,math_y)).y;
int x = m_rect.GetLeft();
if (last_mark-y < 50) continue;
dc.DrawLine(x-3,y,x+3,y);
dc.DrawText(wxString()<<math_y,wxPoint(x-25,y-2/*7*/));
last_mark = y;
}
// axis labels:
wxString label = L("Time (s)");
int text_width = 0;
int text_height = 0;
dc.GetTextExtent(label,&text_width,&text_height);
dc.DrawText(label,wxPoint(0.5*(m_rect.GetRight()+m_rect.GetLeft())-text_width/2.f, m_rect.GetBottom()+25));
label = L("Volumetric speed (mm\u00B3/s)");
dc.GetTextExtent(label,&text_width,&text_height);
dc.DrawRotatedText(label,wxPoint(0,0.5*(m_rect.GetBottom()+m_rect.GetTop())+text_width/2.f),90);
}
void Chart::mouse_right_button_clicked(wxMouseEvent& event) {
if (!manual_points_manipulation)
return;
wxPoint point = event.GetPosition();
int button_index = which_button_is_clicked(point);
if (button_index != -1 && m_buttons.size()>2) {
m_buttons.erase(m_buttons.begin()+button_index);
recalculate_line();
}
}
void Chart::mouse_clicked(wxMouseEvent& event) {
wxPoint point = event.GetPosition();
int button_index = which_button_is_clicked(point);
if ( button_index != -1) {
m_dragged = &m_buttons[button_index];
m_previous_mouse = point;
}
}
void Chart::mouse_moved(wxMouseEvent& event) {
if (!event.Dragging() || !m_dragged) return;
wxPoint pos = event.GetPosition();
wxRect rect = m_rect;
rect.Deflate(side/2.);
if (!(rect.Contains(pos))) { // the mouse left chart area
mouse_left_window(event);
return;
}
int delta_x = pos.x - m_previous_mouse.x;
int delta_y = pos.y - m_previous_mouse.y;
m_dragged->move(fixed_x?0:double(delta_x)/m_rect.GetWidth() * visible_area.m_width,-double(delta_y)/m_rect.GetHeight() * visible_area.m_height);
m_previous_mouse = pos;
recalculate_line();
}
void Chart::mouse_double_clicked(wxMouseEvent& event) {
if (!manual_points_manipulation)
return;
wxPoint point = event.GetPosition();
if (!m_rect.Contains(point)) // the click is outside the chart
return;
m_buttons.push_back(screen_to_math(point));
std::sort(m_buttons.begin(),m_buttons.end());
recalculate_line();
return;
}
void Chart::recalculate_line() {
std::vector<wxPoint> points;
for (auto& but : m_buttons) {
points.push_back(wxPoint(math_to_screen(but.get_pos())));
if (points.size()>1 && points.back().x==points[points.size()-2].x) points.pop_back();
if (points.size()>1 && points.back().x > m_rect.GetRight()) {
points.pop_back();
break;
}
}
std::sort(points.begin(),points.end(),[](wxPoint& a,wxPoint& b) { return a.x < b.x; });
m_line_to_draw.clear();
m_total_volume = 0.f;
// Cubic spline interpolation: see https://en.wikiversity.org/wiki/Cubic_Spline_Interpolation#Methods
const bool boundary_first_derivative = true; // true - first derivative is 0 at the leftmost and rightmost point
// false - second ---- || -------
const int N = points.size()-1; // last point can be accessed as N, we have N+1 total points
std::vector<float> diag(N+1);
std::vector<float> mu(N+1);
std::vector<float> lambda(N+1);
std::vector<float> h(N+1);
std::vector<float> rhs(N+1);
// let's fill in inner equations
for (int i=1;i<=N;++i) h[i] = points[i].x-points[i-1].x;
std::fill(diag.begin(),diag.end(),2.f);
for (int i=1;i<=N-1;++i) {
mu[i] = h[i]/(h[i]+h[i+1]);
lambda[i] = 1.f - mu[i];
rhs[i] = 6 * ( float(points[i+1].y-points[i].y )/(h[i+1]*(points[i+1].x-points[i-1].x)) -
float(points[i].y -points[i-1].y)/(h[i] *(points[i+1].x-points[i-1].x)) );
}
// now fill in the first and last equations, according to boundary conditions:
if (boundary_first_derivative) {
const float endpoints_derivative = 0;
lambda[0] = 1;
mu[N] = 1;
rhs[0] = (6.f/h[1]) * (float(points[0].y-points[1].y)/(points[0].x-points[1].x) - endpoints_derivative);
rhs[N] = (6.f/h[N]) * (endpoints_derivative - float(points[N-1].y-points[N].y)/(points[N-1].x-points[N].x));
}
else {
lambda[0] = 0;
mu[N] = 0;
rhs[0] = 0;
rhs[N] = 0;
}
// the trilinear system is ready to be solved:
for (int i=1;i<=N;++i) {
float multiple = mu[i]/diag[i-1]; // let's subtract proper multiple of above equation
diag[i]-= multiple * lambda[i-1];
rhs[i] -= multiple * rhs[i-1];
}
// now the back substitution (vector mu contains invalid values from now on):
rhs[N] = rhs[N]/diag[N];
for (int i=N-1;i>=0;--i)
rhs[i] = (rhs[i]-lambda[i]*rhs[i+1])/diag[i];
unsigned int i=1;
float y=0.f;
for (int x=m_rect.GetLeft(); x<=m_rect.GetRight() ; ++x) {
if (splines) {
if (i<points.size()-1 && points[i].x < x ) {
++i;
}
if (points[0].x > x)
y = points[0].y;
else
if (points[N].x < x)
y = points[N].y;
else
y = (rhs[i-1]*pow(points[i].x-x,3)+rhs[i]*pow(x-points[i-1].x,3)) / (6*h[i]) +
(points[i-1].y-rhs[i-1]*h[i]*h[i]/6.f) * (points[i].x-x)/h[i] +
(points[i].y -rhs[i] *h[i]*h[i]/6.f) * (x-points[i-1].x)/h[i];
m_line_to_draw.push_back(y);
}
else {
float x_math = screen_to_math(wxPoint(x,0)).m_x;
if (i+2<=points.size() && m_buttons[i+1].get_pos().m_x-0.125 < x_math)
++i;
m_line_to_draw.push_back(math_to_screen(wxPoint2DDouble(x_math,m_buttons[i].get_pos().m_y)).y);
}
m_line_to_draw.back() = std::max(m_line_to_draw.back(), m_rect.GetTop()-1);
m_line_to_draw.back() = std::min(m_line_to_draw.back(), m_rect.GetBottom()-1);
m_total_volume += (m_rect.GetBottom() - m_line_to_draw.back()) * (visible_area.m_width / m_rect.GetWidth()) * (visible_area.m_height / m_rect.GetHeight());
}
wxPostEvent(this->GetParent(), wxCommandEvent(EVT_WIPE_TOWER_CHART_CHANGED));
Refresh();
}
std::vector<float> Chart::get_ramming_speed(float sampling) const {
std::vector<float> speeds_out;
const int number_of_samples = std::round( visible_area.m_width / sampling);
if (number_of_samples>0) {
const int dx = (m_line_to_draw.size()-1) / number_of_samples;
for (int j=0;j<number_of_samples;++j) {
float left = screen_to_math(wxPoint(0,m_line_to_draw[j*dx])).m_y;
float right = screen_to_math(wxPoint(0,m_line_to_draw[(j+1)*dx])).m_y;
speeds_out.push_back((left+right)/2.f);
}
}
return speeds_out;
}
std::vector<std::pair<float,float>> Chart::get_buttons() const {
std::vector<std::pair<float, float>> buttons_out;
for (const auto& button : m_buttons)
buttons_out.push_back(std::make_pair(button.get_pos().m_x,button.get_pos().m_y));
return buttons_out;
}
BEGIN_EVENT_TABLE(Chart, wxWindow)
EVT_MOTION(Chart::mouse_moved)
EVT_LEFT_DOWN(Chart::mouse_clicked)
EVT_LEFT_UP(Chart::mouse_released)
EVT_LEFT_DCLICK(Chart::mouse_double_clicked)
EVT_RIGHT_DOWN(Chart::mouse_right_button_clicked)
EVT_LEAVE_WINDOW(Chart::mouse_left_window)
EVT_PAINT(Chart::paint_event)
END_EVENT_TABLE()

View file

@ -0,0 +1,115 @@
#ifndef RAMMING_CHART_H_
#define RAMMING_CHART_H_
#include <vector>
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
wxDECLARE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent);
class Chart : public wxWindow {
public:
Chart(wxWindow* parent, wxRect rect,const std::vector<std::pair<float,float>>& initial_buttons,int ramming_speed_size, float sampling) :
wxWindow(parent,wxID_ANY,rect.GetTopLeft(),rect.GetSize())
{
SetBackgroundStyle(wxBG_STYLE_PAINT);
m_rect = wxRect(wxPoint(50,0),rect.GetSize()-wxSize(50,50));
visible_area = wxRect2DDouble(0.0, 0.0, sampling*ramming_speed_size, 20.);
m_buttons.clear();
if (initial_buttons.size()>0)
for (const auto& pair : initial_buttons)
m_buttons.push_back(wxPoint2DDouble(pair.first,pair.second));
recalculate_line();
}
void set_xy_range(float x,float y) {
x = int(x/0.5) * 0.5;
if (x>=0) visible_area.SetRight(x);
if (y>=0) visible_area.SetBottom(y);
recalculate_line();
}
float get_volume() const { return m_total_volume; }
float get_time() const { return visible_area.m_width; }
std::vector<float> get_ramming_speed(float sampling) const; //returns sampled ramming speed
std::vector<std::pair<float,float>> get_buttons() const; // returns buttons position
void draw();
void mouse_clicked(wxMouseEvent& event);
void mouse_right_button_clicked(wxMouseEvent& event);
void mouse_moved(wxMouseEvent& event);
void mouse_double_clicked(wxMouseEvent& event);
void mouse_left_window(wxMouseEvent&) { m_dragged = nullptr; }
void mouse_released(wxMouseEvent&) { m_dragged = nullptr; }
void paint_event(wxPaintEvent&) { draw(); }
DECLARE_EVENT_TABLE()
private:
static const bool fixed_x = true;
static const bool splines = true;
static const bool manual_points_manipulation = false;
static const int side = 10; // side of draggable button
class ButtonToDrag {
public:
bool operator<(const ButtonToDrag& a) const { return m_pos.m_x < a.m_pos.m_x; }
ButtonToDrag(wxPoint2DDouble pos) : m_pos{pos} {};
wxPoint2DDouble get_pos() const { return m_pos; }
void move(double x,double y) { m_pos.m_x+=x; m_pos.m_y+=y; }
private:
wxPoint2DDouble m_pos; // position in math coordinates
};
wxPoint math_to_screen(const wxPoint2DDouble& math) const {
wxPoint screen;
screen.x = (math.m_x-visible_area.m_x) * (m_rect.GetWidth() / visible_area.m_width );
screen.y = (math.m_y-visible_area.m_y) * (m_rect.GetHeight() / visible_area.m_height );
screen.y *= -1;
screen += m_rect.GetLeftBottom();
return screen;
}
wxPoint2DDouble screen_to_math(const wxPoint& screen) const {
wxPoint2DDouble math = screen;
math -= m_rect.GetLeftBottom();
math.m_y *= -1;
math.m_x *= visible_area.m_width / m_rect.GetWidth(); // scales to [0;1]x[0,1]
math.m_y *= visible_area.m_height / m_rect.GetHeight();
return (math+visible_area.GetLeftTop());
}
int which_button_is_clicked(const wxPoint& point) const {
if (!m_rect.Contains(point))
return -1;
for (unsigned int i=0;i<m_buttons.size();++i) {
wxRect rect(math_to_screen(m_buttons[i].get_pos())-wxPoint(side/2.,side/2.),wxSize(side,side)); // bounding rectangle of this button
if ( rect.Contains(point) )
return i;
}
return (-1);
}
void recalculate_line();
void recalculate_volume();
wxRect m_rect; // rectangle on screen the chart is mapped into (screen coordinates)
wxPoint m_previous_mouse;
std::vector<ButtonToDrag> m_buttons;
std::vector<int> m_line_to_draw;
wxRect2DDouble visible_area;
ButtonToDrag* m_dragged = nullptr;
float m_total_volume = 0.f;
};
#endif // RAMMING_CHART_H_

View file

@ -3,9 +3,11 @@
#include "PresetBundle.hpp"
#include "PresetHints.hpp"
#include "../../libslic3r/Utils.hpp"
#include "slic3r/Utils/Http.hpp"
#include "slic3r/Utils/OctoPrint.hpp"
#include "BonjourDialog.hpp"
#include "WipeTowerDialog.hpp"
#include <wx/app.h>
#include <wx/button.h>
@ -20,6 +22,7 @@
#include <wx/filedlg.h>
#include <boost/algorithm/string/predicate.hpp>
#include "wxExtensions.hpp"
namespace Slic3r {
namespace GUI {
@ -37,7 +40,42 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
// preset chooser
m_presets_choice = new wxBitmapComboBox(panel, wxID_ANY, "", wxDefaultPosition, wxSize(270, -1), 0, 0,wxCB_READONLY);
/*
m_cc_presets_choice = new wxComboCtrl(panel, wxID_ANY, L(""), wxDefaultPosition, wxDefaultSize, wxCB_READONLY);
wxDataViewTreeCtrlComboPopup* popup = new wxDataViewTreeCtrlComboPopup;
if (popup != nullptr)
{
// FIXME If the following line is removed, the combo box popup list will not react to mouse clicks.
// On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10.
// m_cc_presets_choice->UseAltPopupWindow();
// m_cc_presets_choice->EnablePopupAnimation(false);
m_cc_presets_choice->SetPopupControl(popup);
popup->SetStringValue(from_u8("Text1"));
popup->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this, popup](wxCommandEvent& evt)
{
auto selected = popup->GetItemText(popup->GetSelection());
if (selected != _(L("System presets")) && selected != _(L("Default presets")))
{
m_cc_presets_choice->SetText(selected);
std::string selected_string = selected.ToUTF8().data();
#ifdef __APPLE__
#else
select_preset(selected_string);
#endif
}
});
// popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
// popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
auto icons = new wxImageList(16, 16, true, 1);
popup->SetImageList(icons);
icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG));
icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG));
}
*/
auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
//buttons
@ -82,11 +120,44 @@ void Tab::create_preset_tab(PresetBundle *preset_bundle)
m_hsizer->AddSpacer(64);
m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL);
m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL);
// m_hsizer->AddSpacer(64);
// m_hsizer->Add(m_cc_presets_choice, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3);
//Horizontal sizer to hold the tree and the selected page.
m_hsizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_hsizer, 1, wxEXPAND, 0);
/*
//temporary left vertical sizer
m_left_sizer = new wxBoxSizer(wxVERTICAL);
m_hsizer->Add(m_left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 3);
// tree
m_presetctrl = new wxDataViewTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(200, -1), wxDV_NO_HEADER);
m_left_sizer->Add(m_presetctrl, 1, wxEXPAND);
m_preset_icons = new wxImageList(16, 16, true, 1);
m_presetctrl->SetImageList(m_preset_icons);
m_preset_icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG));
m_preset_icons->Add(*new wxIcon(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG));
m_presetctrl->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxCommandEvent& evt)
{
auto selected = m_presetctrl->GetItemText(m_presetctrl->GetSelection());
if (selected != _(L("System presets")) && selected != _(L("Default presets")))
{
std::string selected_string = selected.ToUTF8().data();
#ifdef __APPLE__
#else
select_preset(selected_string);
#endif
}
});
*/
//left vertical sizer
m_left_sizer = new wxBoxSizer(wxVERTICAL);
m_hsizer->Add(m_left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 3);
@ -134,11 +205,10 @@ void Tab::load_initial_data()
{
m_config = &m_presets->get_edited_preset().config;
m_nonsys_btn_icon = m_presets->get_selected_preset_parent() == nullptr ?
"bullet_white.png" :
wxMSW ? "sys_unlock.png" : "lock_open.png";
"bullet_white.png" : "sys_unlock.png";
}
PageShp Tab::add_options_page(wxString title, std::string icon, bool is_extruder_pages/* = false*/)
PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages/* = false*/)
{
// Index of icon in an icon list $self->{icons}.
auto icon_idx = 0;
@ -258,27 +328,36 @@ void Tab::update_changed_ui()
m_dirty_options = dirty_options;
}
Freeze();
//update options "decoration"
for (const auto opt_key : m_full_options_list)
{
bool is_nonsys_value = false;
bool is_modified_value = true;
std::string sys_icon = wxMSW ? "sys_lock.png" : "lock.png";
std::string icon = wxMSW ? "action_undo.png" : "arrow_undo.png";
wxColour& color = *get_sys_label_clr();
std::string sys_icon = "sys_lock.png";
std::string icon = "action_undo.png";
wxColour color = get_sys_label_clr();
if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()) {
is_nonsys_value = true;
sys_icon = m_nonsys_btn_icon;
if(find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end())
color = wxSYS_COLOUR_WINDOWTEXT;
color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
else
color = *get_modified_label_clr();
color = get_modified_label_clr();
}
if (find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end())
{
is_modified_value = false;
icon = "bullet_white.png";
}
if (opt_key == "bed_shape" || opt_key == "compatible_printers") {
if (m_colored_Label != nullptr) {
m_colored_Label->SetForegroundColour(color);
m_colored_Label->Refresh(true);
}
continue;
}
Field* field = get_field(opt_key);
if (field == nullptr) continue;
field->m_is_nonsys_value = is_nonsys_value;
@ -290,6 +369,7 @@ void Tab::update_changed_ui()
field->m_Label->Refresh(true);
}
}
Thaw();
wxTheApp->CallAfter([this]() {
update_changed_tree_ui();
@ -343,7 +423,7 @@ void Tab::update_sys_ui_after_sel_preset()
field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(m_nonsys_btn_icon)), wxBITMAP_TYPE_PNG));
field->m_is_nonsys_value = true;
if (field->m_Label != nullptr){
field->m_Label->SetForegroundColour(wxSYS_COLOUR_WINDOWTEXT);
field->m_Label->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
field->m_Label->Refresh(true);
}
}
@ -351,13 +431,20 @@ void Tab::update_sys_ui_after_sel_preset()
m_sys_options.resize(0);
}
void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page)
{
if (sys_page && find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end())
sys_page = false;
if (!modified_page && find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end())
modified_page = true;
}
void Tab::update_changed_tree_ui()
{
auto cur_item = m_treectrl->GetFirstVisibleItem();
auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection());
while (cur_item){
auto title = m_treectrl->GetItemText(cur_item);
int i=0;
for (auto page : m_pages)
{
if (page->title() != title)
@ -367,30 +454,27 @@ void Tab::update_changed_tree_ui()
if (title == _("General")){
std::initializer_list<const char*> optional_keys{ "extruders_count", "bed_shape" };
for (auto &opt_key : optional_keys) {
if (sys_page && find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end())
sys_page = false;
if (!modified_page && find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end())
modified_page = true;
get_sys_and_mod_flags(opt_key, sys_page, modified_page);
}
}
if (title == _("Dependencies")){
get_sys_and_mod_flags("compatible_printers", sys_page, modified_page);
}
for (auto group : page->m_optgroups)
{
for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
const std::string& opt_key = it->first;
if (sys_page && find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end())
sys_page = false;
if (!modified_page && find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end())
modified_page = true;
}
if (!sys_page && modified_page)
break;
for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
const std::string& opt_key = it->first;
get_sys_and_mod_flags(opt_key, sys_page, modified_page);
}
}
if (sys_page)
m_treectrl->SetItemTextColour(cur_item, *get_sys_label_clr());
m_treectrl->SetItemTextColour(cur_item, get_sys_label_clr());
else if (modified_page)
m_treectrl->SetItemTextColour(cur_item, *get_modified_label_clr());
m_treectrl->SetItemTextColour(cur_item, get_modified_label_clr());
else
m_treectrl->SetItemTextColour(cur_item, wxSYS_COLOUR_WINDOWTEXT);
m_treectrl->SetItemTextColour(cur_item, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
page->m_is_nonsys_values = !sys_page;
page->m_is_modified_values = modified_page;
@ -409,10 +493,8 @@ void Tab::update_changed_tree_ui()
void Tab::update_undo_buttons()
{
const std::string& undo_icon = !m_is_modified_values ? "bullet_white.png" :
wxMSW ? "action_undo.png" : "arrow_undo.png";
const std::string& undo_to_sys_icon = m_is_nonsys_values ? m_nonsys_btn_icon :
wxMSW ? "sys_lock.png" : "lock.png";
const std::string& undo_icon = !m_is_modified_values ? "bullet_white.png" : "action_undo.png";
const std::string& undo_to_sys_icon = m_is_nonsys_values ? m_nonsys_btn_icon : "sys_lock.png";
m_undo_btn->SetBitmap(wxBitmap(from_u8(var(undo_icon)), wxBITMAP_TYPE_PNG));
m_undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(undo_to_sys_icon)), wxBITMAP_TYPE_PNG));
@ -434,6 +516,14 @@ void Tab::on_back_to_initial_value()
if (find(m_dirty_options.begin(), m_dirty_options.end(), "bed_shape") != m_dirty_options.end())
group->back_to_initial_value("bed_shape");
}
if (group->title == _("Profile dependencies")){
if (find(m_dirty_options.begin(), m_dirty_options.end(), "compatible_printers") != m_dirty_options.end())
group->back_to_initial_value("compatible_printers");
bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty();
m_compatible_printers_checkbox->SetValue(is_empty);
is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable();
}
for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
const std::string& opt_key = it->first;
if (find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) != m_dirty_options.end())
@ -461,6 +551,14 @@ void Tab::on_back_to_sys_value()
if (find(m_sys_options.begin(), m_sys_options.end(), "bed_shape") == m_sys_options.end())
group->back_to_sys_value("bed_shape");
}
if (group->title == _("Profile dependencies")){
if (find(m_sys_options.begin(), m_sys_options.end(), "compatible_printers") == m_sys_options.end())
group->back_to_sys_value("compatible_printers");
bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty();
m_compatible_printers_checkbox->SetValue(is_empty);
is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable();
}
for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) {
const std::string& opt_key = it->first;
if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end())
@ -478,16 +576,19 @@ void Tab::update_dirty(){
m_presets->update_dirty_ui(m_presets_choice);
on_presets_changed();
update_changed_ui();
// update_dirty_presets(m_cc_presets_choice);
}
void Tab::update_tab_ui()
{
m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets);
// update_tab_presets(m_cc_presets_choice, m_show_incompatible_presets);
// update_presetsctrl(m_presetctrl, m_show_incompatible_presets);
}
// Load a provied DynamicConfig into the tab, modifying the active preset.
// This could be used for example by setting a Wipe Tower position by interactive manipulation in the 3D view.
void Tab::load_config(DynamicPrintConfig config)
void Tab::load_config(const DynamicPrintConfig& config)
{
bool modified = 0;
for(auto opt_key : m_config->diff(config)) {
@ -510,7 +611,7 @@ void Tab::reload_config(){
Thaw();
}
Field* Tab::get_field(t_config_option_key opt_key, int opt_index/* = -1*/) const
Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/) const
{
Field* field = nullptr;
for (auto page : m_pages){
@ -524,7 +625,7 @@ Field* Tab::get_field(t_config_option_key opt_key, int opt_index/* = -1*/) const
// Set a key/value pair on this page. Return true if the value has been modified.
// Currently used for distributing extruders_count over preset pages of Slic3r::GUI::Tab::Printer
// after a preset is loaded.
bool Tab::set_value(t_config_option_key opt_key, boost::any value){
bool Tab::set_value(const t_config_option_key& opt_key, const boost::any& value){
bool changed = false;
for(auto page: m_pages) {
if (page->set_value(opt_key, value))
@ -535,7 +636,7 @@ bool Tab::set_value(t_config_option_key opt_key, boost::any value){
// To be called by custom widgets, load a value into a config,
// update the preset selection boxes (the dirty flags)
void Tab::load_key_value(std::string opt_key, boost::any value)
void Tab::load_key_value(const std::string& opt_key, const boost::any& value)
{
change_opt_value(*m_config, opt_key, value);
// Mark the print & filament enabled if they are compatible with the currently selected preset.
@ -550,7 +651,7 @@ void Tab::load_key_value(std::string opt_key, boost::any value)
extern wxFrame *g_wxMainFrame;
void Tab::on_value_change(std::string opt_key, boost::any value)
void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
{
if (m_event_value_change > 0) {
wxCommandEvent event(m_event_value_change);
@ -565,8 +666,8 @@ void Tab::on_value_change(std::string opt_key, boost::any value)
}
if (opt_key == "fill_density")
{
value = get_optgroup()->get_config_value(*m_config, opt_key);
get_optgroup()->set_value(opt_key, value);
boost::any val = get_optgroup()->get_config_value(*m_config, opt_key);
get_optgroup()->set_value(opt_key, val);
}
if (opt_key == "support_material" || opt_key == "support_material_buildplate_only")
{
@ -583,10 +684,27 @@ void Tab::on_value_change(std::string opt_key, boost::any value)
get_optgroup()->set_value("brim", val);
}
if (opt_key == "wipe_tower" || opt_key == "single_extruder_multi_material" || opt_key == "extruders_count" )
update_wiping_button_visibility();
update();
}
// Show/hide the 'purging volumes' button
void Tab::update_wiping_button_visibility() {
bool wipe_tower_enabled = dynamic_cast<ConfigOptionBool*>( (m_preset_bundle->prints.get_edited_preset().config ).option("wipe_tower"))->value;
bool multiple_extruders = dynamic_cast<ConfigOptionFloats*>((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->values.size() > 1;
bool single_extruder_mm = dynamic_cast<ConfigOptionBool*>( (m_preset_bundle->printers.get_edited_preset().config).option("single_extruder_multi_material"))->value;
if (wipe_tower_enabled && multiple_extruders && single_extruder_mm)
get_wiping_dialog_button()->Show();
else get_wiping_dialog_button()->Hide();
(get_wiping_dialog_button()->GetParent())->Layout();
}
// Call a callback to update the selection of presets on the platter:
// To update the content of the selection boxes,
// to update the filament colors of the selection boxes,
@ -622,6 +740,8 @@ void Tab::update_frequently_changed_parameters()
bool val = m_config->opt_float("brim_width") > 0.0 ? true : false;
get_optgroup()->set_value("brim", val);
update_wiping_button_visibility();
}
void Tab::reload_compatible_printers_widget()
@ -771,7 +891,8 @@ void TabPrint::build()
optgroup->append_single_option_line("wipe_tower_x");
optgroup->append_single_option_line("wipe_tower_y");
optgroup->append_single_option_line("wipe_tower_width");
optgroup->append_single_option_line("wipe_tower_per_color_wipe");
optgroup->append_single_option_line("wipe_tower_rotation_angle");
optgroup->append_single_option_line("wipe_tower_bridging");
optgroup = page->new_optgroup(_(L("Advanced")));
optgroup->append_single_option_line("interface_shells");
@ -837,7 +958,7 @@ void TabPrint::build()
line.widget = [this](wxWindow* parent){
return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn);
};
optgroup->append_line(line);
optgroup->append_line(line, &m_colored_Label);
option = optgroup->get_option("compatible_printers_condition");
option.opt.full_width = true;
@ -1016,53 +1137,40 @@ void TabPrint::update()
}
bool have_perimeters = m_config->opt_int("perimeters") > 0;
std::vector<std::string> vec_enable = { "extra_perimeters", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
"seam_position", "external_perimeters_first", "external_perimeter_extrusion_width",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed" };
for (auto el : vec_enable)
for (auto el : {"extra_perimeters", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
"seam_position", "external_perimeters_first", "external_perimeter_extrusion_width",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed" })
get_field(el)->toggle(have_perimeters);
bool have_infill = m_config->option<ConfigOptionPercent>("fill_density")->value > 0;
vec_enable.resize(0);
vec_enable = { "fill_pattern", "infill_every_layers", "infill_only_where_needed",
"solid_infill_every_layers", "solid_infill_below_area", "infill_extruder" };
// infill_extruder uses the same logic as in Print::extruders()
for (auto el : vec_enable)
for (auto el : {"fill_pattern", "infill_every_layers", "infill_only_where_needed",
"solid_infill_every_layers", "solid_infill_below_area", "infill_extruder" })
get_field(el)->toggle(have_infill);
bool have_solid_infill = m_config->opt_int("top_solid_layers") > 0 || m_config->opt_int("bottom_solid_layers") > 0;
vec_enable.resize(0);
vec_enable = { "external_fill_pattern", "infill_first", "solid_infill_extruder",
"solid_infill_extrusion_width", "solid_infill_speed" };
// solid_infill_extruder uses the same logic as in Print::extruders()
for (auto el : vec_enable)
for (auto el : {"external_fill_pattern", "infill_first", "solid_infill_extruder",
"solid_infill_extrusion_width", "solid_infill_speed" })
get_field(el)->toggle(have_solid_infill);
vec_enable.resize(0);
vec_enable = { "fill_angle", "bridge_angle", "infill_extrusion_width",
"infill_speed", "bridge_speed" };
for (auto el : vec_enable)
for (auto el : {"fill_angle", "bridge_angle", "infill_extrusion_width",
"infill_speed", "bridge_speed" })
get_field(el)->toggle(have_infill || have_solid_infill);
get_field("gap_fill_speed")->toggle(have_perimeters && have_infill);
bool have_top_solid_infill = m_config->opt_int("top_solid_layers") > 0;
vec_enable.resize(0);
vec_enable = { "top_infill_extrusion_width", "top_solid_infill_speed" };
for (auto el : vec_enable)
for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" })
get_field(el)->toggle(have_top_solid_infill);
bool have_default_acceleration = m_config->opt_float("default_acceleration") > 0;
vec_enable.resize(0);
vec_enable = { "perimeter_acceleration", "infill_acceleration",
"bridge_acceleration", "first_layer_acceleration" };
for (auto el : vec_enable)
for (auto el : {"perimeter_acceleration", "infill_acceleration",
"bridge_acceleration", "first_layer_acceleration" })
get_field(el)->toggle(have_default_acceleration);
bool have_skirt = m_config->opt_int("skirts") > 0 || m_config->opt_float("min_skirt_length") > 0;
vec_enable.resize(0);
vec_enable = { "skirt_distance", "skirt_height" };
for (auto el : vec_enable)
for (auto el : { "skirt_distance", "skirt_height" })
get_field(el)->toggle(have_skirt);
bool have_brim = m_config->opt_float("brim_width") > 0;
@ -1073,18 +1181,14 @@ void TabPrint::update()
bool have_support_material = m_config->opt_bool("support_material") || have_raft;
bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0;
bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0;
vec_enable.resize(0);
vec_enable = { "support_material_threshold", "support_material_pattern", "support_material_with_sheath",
for (auto el : {"support_material_threshold", "support_material_pattern", "support_material_with_sheath",
"support_material_spacing", "support_material_angle", "support_material_interface_layers",
"dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance",
"support_material_xy_spacing" };
for (auto el : vec_enable)
"support_material_xy_spacing" })
get_field(el)->toggle(have_support_material);
vec_enable.resize(0);
vec_enable = { "support_material_interface_spacing", "support_material_interface_extruder",
"support_material_interface_speed", "support_material_interface_contact_loops" };
for (auto el : vec_enable)
for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder",
"support_material_interface_speed", "support_material_interface_contact_loops" })
get_field(el)->toggle(have_support_material && have_support_interface);
get_field("support_material_synchronize_layers")->toggle(have_support_soluble);
@ -1093,18 +1197,14 @@ void TabPrint::update()
get_field("support_material_speed")->toggle(have_support_material || have_brim || have_skirt);
bool have_sequential_printing = m_config->opt_bool("complete_objects");
vec_enable.resize(0);
vec_enable = { "extruder_clearance_radius", "extruder_clearance_height" };
for (auto el : vec_enable)
for (auto el : { "extruder_clearance_radius", "extruder_clearance_height" })
get_field(el)->toggle(have_sequential_printing);
bool have_ooze_prevention = m_config->opt_bool("ooze_prevention");
get_field("standby_temperature_delta")->toggle(have_ooze_prevention);
bool have_wipe_tower = m_config->opt_bool("wipe_tower");
vec_enable.resize(0);
vec_enable = { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_per_color_wipe" };
for (auto el : vec_enable)
for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging"})
get_field(el)->toggle(have_wipe_tower);
m_recommended_thin_wall_thickness_description_line->SetText(
@ -1184,7 +1284,29 @@ void TabFilament::build()
};
optgroup->append_line(line);
page = add_options_page(_(L("Custom G-code")), "cog.png");
optgroup = page->new_optgroup(_(L("Toolchange behaviour")));
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_time");
line = { _(L("Ramming")), "" };
line.widget = [this](wxWindow* parent){
auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+"\u2026", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(ramming_dialog_btn);
ramming_dialog_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
RammingDialog dlg(this,(m_config->option<ConfigOptionStrings>("filament_ramming_parameters"))->get_at(0));
if (dlg.ShowModal() == wxID_OK)
(m_config->option<ConfigOptionStrings>("filament_ramming_parameters"))->get_at(0) = dlg.get_parameters();
}));
return sizer;
};
optgroup->append_line(line);
page = add_options_page(_(L("Custom G-code")), "cog.png");
optgroup = page->new_optgroup(_(L("Start G-code")), 0);
Option option = optgroup->get_option("start_filament_gcode");
option.opt.full_width = true;
@ -1211,7 +1333,7 @@ void TabFilament::build()
line.widget = [this](wxWindow* parent){
return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn);
};
optgroup->append_line(line);
optgroup->append_line(line, &m_colored_Label);
option = optgroup->get_option("compatible_printers_condition");
option.opt.full_width = true;
@ -1241,13 +1363,10 @@ void TabFilament::update()
bool cooling = m_config->opt_bool("cooling", 0);
bool fan_always_on = cooling || m_config->opt_bool("fan_always_on", 0);
std::vector<std::string> vec_enable = { "max_fan_speed", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed" };
for (auto el : vec_enable)
for (auto el : { "max_fan_speed", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed" })
get_field(el)->toggle(cooling);
vec_enable.resize(0);
vec_enable = { "min_fan_speed", "disable_fan_first_layers" };
for (auto el : vec_enable)
for (auto el : { "min_fan_speed", "disable_fan_first_layers" })
get_field(el)->toggle(fan_always_on);
}
@ -1308,7 +1427,7 @@ void TabPrinter::build()
return sizer;
};
optgroup->append_line(line);
optgroup->append_line(line, &m_colored_Label);
optgroup->append_single_option_line("max_print_height");
optgroup->append_single_option_line("z_offset");
@ -1326,9 +1445,11 @@ void TabPrinter::build()
optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value){
size_t extruders_count = boost::any_cast<int>(optgroup->get_value("extruders_count"));
wxTheApp->CallAfter([this, opt_key, value, extruders_count](){
if (opt_key.compare("extruders_count")==0) {
if (opt_key.compare("extruders_count")==0 || opt_key.compare("single_extruder_multi_material")==0) {
extruders_count_changed(extruders_count);
update_dirty();
if (opt_key.compare("single_extruder_multi_material")==0) // the single_extruder_multimaterial was added to force pages
on_value_change(opt_key, value); // rebuild - let's make sure the on_value_change is not skipped
}
else {
update_dirty();
@ -1337,6 +1458,7 @@ void TabPrinter::build()
});
};
if (!m_no_controller)
{
optgroup = page->new_optgroup(_(L("USB/Serial connection")));
@ -1607,6 +1729,25 @@ void TabPrinter::build_extruder_pages(){
for (auto page_extruder : m_extruder_pages)
m_pages.push_back(page_extruder);
m_pages.push_back(page_note);
{
// if we have a single extruder MM setup, add a page with configuration options:
for (int i=0;i<m_pages.size();++i) // first make sure it's not there already
if (m_pages[i]->title().find(_(L("Single extruder MM setup"))) != std::string::npos) {
m_pages.erase(m_pages.begin()+i);
break;
}
if ( m_extruder_pages.size()>1 && m_config->opt_bool("single_extruder_multi_material")) {
// create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves
auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png",true);
auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters")));
optgroup->append_single_option_line("cooling_tube_retraction");
optgroup->append_single_option_line("cooling_tube_length");
optgroup->append_single_option_line("parking_pos_retraction");
m_pages.insert(m_pages.begin()+1,page);
}
}
rebuild_page_tree();
}
@ -1714,9 +1855,7 @@ void Tab::load_current_preset()
// Reload preset pages with the new configuration values.
reload_config();
const Preset* parent = m_presets->get_selected_preset_parent();
m_nonsys_btn_icon = parent == nullptr ?
"bullet_white.png" :
wxMSW ? "sys_unlock.png" : "lock_open.png";
m_nonsys_btn_icon = parent == nullptr ? "bullet_white.png" : "sys_unlock.png";
// use CallAfter because some field triggers schedule on_change calls using CallAfter,
// and we don't want them to be called after this update_dirty() as they would mark the
@ -1773,7 +1912,7 @@ void Tab::rebuild_page_tree()
// Called by the UI combo box when the user switches profiles.
// Select a preset by a name.If !defined(name), then the default preset is selected.
// If the current profile is modified, user is asked to save the changes.
void Tab::select_preset(const std::string &preset_name /*= ""*/)
void Tab::select_preset(const std::string& preset_name /*= ""*/)
{
std::string name = preset_name;
auto force = false;
@ -1840,7 +1979,7 @@ void Tab::select_preset(const std::string &preset_name /*= ""*/)
// If the current preset is dirty, the user is asked whether the changes may be discarded.
// if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned.
bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, std::string new_printer_name /*= ""*/)
bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/)
{
if (presets == nullptr) presets = m_presets;
// Display a dialog showing the dirty options in a human readable form.
@ -2048,6 +2187,7 @@ wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox
if ((*checkbox)->GetValue())
load_key_value("compatible_printers", std::vector<std::string> {});
get_field("compatible_printers_condition")->toggle((*checkbox)->GetValue());
update_changed_ui();
}) );
(*btn)->Bind(wxEVT_BUTTON, ([this, parent, checkbox, btn](wxCommandEvent e)
@ -2090,18 +2230,186 @@ wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox
}
// All printers have been made compatible with this preset.
load_key_value("compatible_printers", value);
update_changed_ui();
}
}));
return sizer;
}
void Tab::update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible)
{
if (ui == nullptr)
return;
ui->Freeze();
ui->DeleteAllItems();
auto presets = m_presets->get_presets();
auto idx_selected = m_presets->get_idx_selected();
auto suffix_modified = m_presets->get_suffix_modified();
int icon_compatible = 0;
int icon_incompatible = 1;
int cnt_items = 0;
auto root_sys = ui->AppendContainer(wxDataViewItem(0), _(L("System presets")));
auto root_def = ui->AppendContainer(wxDataViewItem(0), _(L("Default presets")));
auto show_def = get_app_config()->get("no_defaults")[0] != '1';
for (size_t i = presets.front().is_visible ? 0 : 1; i < presets.size(); ++i) {
const Preset &preset = presets[i];
if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected))
continue;
auto preset_name = wxString::FromUTF8((preset.name + (preset.is_dirty ? suffix_modified : "")).c_str());
wxDataViewItem item;
if (preset.is_system)
item = ui->AppendItem(root_sys, preset_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
else if (show_def && preset.is_default)
item = ui->AppendItem(root_def, preset_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
else
{
auto parent = m_presets->get_preset_parent(preset);
if (parent == nullptr)
item = ui->AppendItem(root_def, preset_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
else
{
auto parent_name = parent->name;
wxDataViewTreeStoreContainerNode *node = ui->GetStore()->FindContainerNode(root_sys);
if (node)
{
wxDataViewTreeStoreNodeList::iterator iter;
for (iter = node->GetChildren().begin(); iter != node->GetChildren().end(); iter++)
{
wxDataViewTreeStoreNode* child = *iter;
auto child_item = child->GetItem();
auto item_text = ui->GetItemText(child_item);
if (item_text == parent_name)
{
auto added_child = ui->AppendItem(child->GetItem(), preset_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
if (!added_child){
ui->DeleteItem(child->GetItem());
auto new_parent = ui->AppendContainer(root_sys, parent_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
ui->AppendItem(new_parent, preset_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
}
break;
}
}
}
}
}
cnt_items++;
if (i == idx_selected){
ui->Select(item);
m_cc_presets_choice->SetText(preset_name);
}
}
if (ui->GetStore()->GetChildCount(root_def) == 0)
ui->DeleteItem(root_def);
ui->Thaw();
}
void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible)
{
if (ui == nullptr)
return;
ui->Freeze();
ui->Clear();
auto presets = m_presets->get_presets();
auto idx_selected = m_presets->get_idx_selected();
auto suffix_modified = m_presets->get_suffix_modified();
int icon_compatible = 0;
int icon_incompatible = 1;
int cnt_items = 0;
wxDataViewTreeCtrlComboPopup* popup = wxDynamicCast(m_cc_presets_choice->GetPopupControl(), wxDataViewTreeCtrlComboPopup);
if (popup != nullptr)
{
popup->DeleteAllItems();
auto root_sys = popup->AppendContainer(wxDataViewItem(0), _(L("System presets")));
auto root_def = popup->AppendContainer(wxDataViewItem(0), _(L("Default presets")));
auto show_def = get_app_config()->get("no_defaults")[0] != '1';
for (size_t i = presets.front().is_visible ? 0 : 1; i < presets.size(); ++i) {
const Preset &preset = presets[i];
if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected))
continue;
auto preset_name = wxString::FromUTF8((preset.name + (preset.is_dirty ? suffix_modified : "")).c_str());
wxDataViewItem item;
if (preset.is_system)
item = popup->AppendItem(root_sys, preset_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
else if (show_def && preset.is_default)
item = popup->AppendItem(root_def, preset_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
else
{
auto parent = m_presets->get_preset_parent(preset);
if (parent == nullptr)
item = popup->AppendItem(root_def, preset_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
else
{
auto parent_name = parent->name;
wxDataViewTreeStoreContainerNode *node = popup->GetStore()->FindContainerNode(root_sys);
if (node)
{
wxDataViewTreeStoreNodeList::iterator iter;
for (iter = node->GetChildren().begin(); iter != node->GetChildren().end(); iter++)
{
wxDataViewTreeStoreNode* child = *iter;
auto child_item = child->GetItem();
auto item_text = popup->GetItemText(child_item);
if (item_text == parent_name)
{
auto added_child = popup->AppendItem(child->GetItem(), preset_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
if (!added_child){
popup->DeleteItem(child->GetItem());
auto new_parent = popup->AppendContainer(root_sys, parent_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
popup->AppendItem(new_parent, preset_name,
preset.is_compatible ? icon_compatible : icon_incompatible);
}
break;
}
}
}
}
}
cnt_items++;
if (i == idx_selected){
popup->Select(item);
m_cc_presets_choice->SetText(preset_name);
}
}
if (popup->GetStore()->GetChildCount(root_def) == 0)
popup->DeleteItem(root_def);
}
ui->Thaw();
}
void Page::reload_config()
{
for (auto group : m_optgroups)
group->reload_config();
}
Field* Page::get_field(t_config_option_key opt_key, int opt_index/* = -1*/) const
Field* Page::get_field(const t_config_option_key& opt_key, int opt_index /*= -1*/) const
{
Field* field = nullptr;
for (auto opt : m_optgroups){
@ -2112,7 +2420,7 @@ Field* Page::get_field(t_config_option_key opt_key, int opt_index/* = -1*/) cons
return field;
}
bool Page::set_value(t_config_option_key opt_key, boost::any value){
bool Page::set_value(const t_config_option_key& opt_key, const boost::any& value){
bool changed = false;
for(auto optgroup: m_optgroups) {
if (optgroup->set_value(opt_key, value))
@ -2122,7 +2430,7 @@ bool Page::set_value(t_config_option_key opt_key, boost::any value){
}
// package Slic3r::GUI::Tab::Page;
ConfigOptionsGroupShp Page::new_optgroup(wxString title, int noncommon_label_width /*= -1*/)
ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_label_width /*= -1*/)
{
//! config_ have to be "right"
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(this, title, m_config, true);
@ -2163,7 +2471,7 @@ ConfigOptionsGroupShp Page::new_optgroup(wxString title, int noncommon_label_wid
return optgroup;
}
void SavePresetWindow::build(wxString title, std::string default_name, std::vector<std::string> &values)
void SavePresetWindow::build(const wxString& title, const std::string& default_name, std::vector<std::string> &values)
{
auto text = new wxStaticText(this, wxID_ANY, _(L("Save ")) + title + _(L(" as:")),
wxDefaultPosition, wxDefaultSize);

View file

@ -21,6 +21,7 @@
#include <wx/treectrl.h>
#include <wx/imaglist.h>
#include <wx/statbox.h>
#include <wx/dataview.h>
#include <map>
#include <vector>
@ -67,9 +68,9 @@ public:
size_t iconID() const { return m_iconID; }
void set_config(DynamicPrintConfig* config_in) { m_config = config_in; }
void reload_config();
Field* get_field(t_config_option_key opt_key, int opt_index = -1) const;
bool set_value(t_config_option_key opt_key, boost::any value);
ConfigOptionsGroupShp new_optgroup(wxString title, int noncommon_label_width = -1);
Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const;
bool set_value(const t_config_option_key& opt_key, const boost::any& value);
ConfigOptionsGroupShp new_optgroup(const wxString& title, int noncommon_label_width = -1);
};
// Slic3r::GUI::Tab;
@ -95,6 +96,9 @@ protected:
wxButton* m_compatible_printers_btn;
wxButton* m_undo_btn;
wxButton* m_undo_to_sys_btn;
wxComboCtrl* m_cc_presets_choice;
wxDataViewTreeCtrl* m_presetctrl;
wxImageList* m_preset_icons;
int m_icon_count;
std::map<std::string, size_t> m_icon_index; // Map from an icon file name to its index
@ -122,10 +126,11 @@ public:
DynamicPrintConfig* m_config;
std::string m_nonsys_btn_icon;
ogStaticText* m_parent_preset_description_line;
wxStaticText* m_colored_Label = nullptr;
public:
Tab() {}
Tab(wxNotebook* parent, wxString title, const char* name, bool no_controller) :
Tab(wxNotebook* parent, const wxString& title, const char* name, bool no_controller) :
m_parent(parent), m_title(title), m_name(name), m_no_controller(no_controller) {
Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL);
get_tabs_list().push_back(this);
@ -143,11 +148,12 @@ public:
void create_preset_tab(PresetBundle *preset_bundle);
void load_current_preset();
void rebuild_page_tree();
void select_preset(const std::string &preset_name = "");
bool may_discard_current_dirty_preset(PresetCollection* presets = nullptr, std::string new_printer_name = "");
void select_preset(const std::string& preset_name = "");
bool may_discard_current_dirty_preset(PresetCollection* presets = nullptr, const std::string& new_printer_name = "");
wxSizer* compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn);
void load_key_value(std::string opt_key, boost::any value);
void update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible);
void load_key_value(const std::string& opt_key, const boost::any& value);
void reload_compatible_printers_widget();
void OnTreeSelChange(wxTreeEvent& event);
@ -161,13 +167,14 @@ public:
void update_changed_ui();
void update_full_options_list();
void update_sys_ui_after_sel_preset();
void get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page);
void update_changed_tree_ui();
void update_undo_buttons();
void on_back_to_initial_value();
void on_back_to_sys_value();
PageShp add_options_page(wxString title, std::string icon, bool is_extruder_pages = false);
PageShp add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages = false);
virtual void OnActivate(){}
virtual void on_preset_loaded(){}
@ -176,10 +183,10 @@ public:
void load_initial_data();
void update_dirty();
void update_tab_ui();
void load_config(DynamicPrintConfig config);
void load_config(const DynamicPrintConfig& config);
virtual void reload_config();
Field* get_field(t_config_option_key opt_key, int opt_index = -1) const;
bool set_value(t_config_option_key opt_key, boost::any value);
Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const;
bool set_value(const t_config_option_key& opt_key, const boost::any& value);
wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText);
bool current_preset_is_dirty();
DynamicPrintConfig* get_config() { return m_config; }
@ -189,11 +196,13 @@ public:
}
std::vector<std::string> get_dependent_tabs() { return m_reload_dependent_tabs; }
void on_value_change(std::string opt_key, boost::any value);
void on_value_change(const std::string& opt_key, const boost::any& value);
protected:
void on_presets_changed();
void update_frequently_changed_parameters();
void update_wiping_button_visibility();
void update_tab_presets(wxComboCtrl* ui, bool show_incompatible);
};
//Slic3r::GUI::Tab::Print;
@ -264,7 +273,7 @@ public:
std::string m_chosen_name;
wxComboBox* m_combo;
void build(wxString title, std::string default_name, std::vector<std::string> &values);
void build(const wxString& title, const std::string& default_name, std::vector<std::string> &values);
void accept();
std::string get_name() { return m_chosen_name; }
};

View file

@ -0,0 +1,343 @@
#include <algorithm>
#include <sstream>
#include "WipeTowerDialog.hpp"
#include <wx/sizer.h>
//! macro used to mark string used at localization,
//! return same string
#define L(s) s
RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters)
: wxDialog(parent, wxID_ANY, _(L("Ramming customization")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/)
{
m_panel_ramming = new RammingPanel(this,parameters);
// Not found another way of getting the background colours of RammingDialog, RammingPanel and Chart correct than setting
// them all explicitely. Reading the parent colour yielded colour that didn't really match it, no wxSYS_COLOUR_... matched
// colour used for the dialog. Same issue (and "solution") here : https://forums.wxwidgets.org/viewtopic.php?f=1&t=39608
// Whoever can fix this, feel free to do so.
this-> SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK));
m_panel_ramming->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK));
m_panel_ramming->Show(true);
this->Show();
auto main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(m_panel_ramming, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5);
main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxBOTTOM, 10);
SetSizer(main_sizer);
main_sizer->SetSizeHints(this);
this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) {
m_output_data = m_panel_ramming->get_parameters();
EndModal(wxID_OK);
},wxID_OK);
this->Show();
wxMessageDialog(this,_(L("Ramming denotes the rapid extrusion just before a tool change in a single-extruder MM printer. Its purpose is to "
"properly shape the end of the unloaded filament so it does not prevent insertion of the new filament and can itself "
"be reinserted later. This phase is important and different materials can require different extrusion speeds to get "
"the good shape. For this reason, the extrusion rates during ramming are adjustable.\n\nThis is an expert-level "
"setting, incorrect adjustment will likely lead to jams, extruder wheel grinding into filament etc.")),_(L("Warning")),wxOK|wxICON_EXCLAMATION).ShowModal();
}
RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize/*,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED*/)
{
auto sizer_chart = new wxBoxSizer(wxVERTICAL);
auto sizer_param = new wxBoxSizer(wxVERTICAL);
std::stringstream stream{ parameters };
stream >> m_ramming_line_width_multiplicator >> m_ramming_step_multiplicator;
int ramming_speed_size = 0;
float dummy = 0.f;
while (stream >> dummy)
++ramming_speed_size;
stream.clear();
stream.get();
std::vector<std::pair<float, float>> buttons;
float x = 0.f;
float y = 0.f;
while (stream >> x >> y)
buttons.push_back(std::make_pair(x, y));
m_chart = new Chart(this, wxRect(10, 10, 480, 360), buttons, ramming_speed_size, 0.25f);
m_chart->SetBackgroundColour(parent->GetBackgroundColour()); // see comment in RammingDialog constructor
sizer_chart->Add(m_chart, 0, wxALL, 5);
m_widget_time = new wxSpinCtrlDouble(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,0.,5.0,3.,0.5);
m_widget_volume = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,0,10000,0);
m_widget_ramming_line_width_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,10,200,100);
m_widget_ramming_step_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,10,200,100);
auto gsizer_param = new wxFlexGridSizer(2, 5, 15);
gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total ramming time (s):")))), 0, wxALIGN_CENTER_VERTICAL);
gsizer_param->Add(m_widget_time);
gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total rammed volume (mm"))+"\u00B3):")), 0, wxALIGN_CENTER_VERTICAL);
gsizer_param->Add(m_widget_volume);
gsizer_param->AddSpacer(20);
gsizer_param->AddSpacer(20);
gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line width (%):")))), 0, wxALIGN_CENTER_VERTICAL);
gsizer_param->Add(m_widget_ramming_line_width_multiplicator);
gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line spacing (%):")))), 0, wxALIGN_CENTER_VERTICAL);
gsizer_param->Add(m_widget_ramming_step_multiplicator);
sizer_param->Add(gsizer_param, 0, wxTOP, 100);
m_widget_time->SetValue(m_chart->get_time());
m_widget_time->SetDigits(2);
m_widget_volume->SetValue(m_chart->get_volume());
m_widget_volume->Disable();
m_widget_ramming_line_width_multiplicator->SetValue(m_ramming_line_width_multiplicator);
m_widget_ramming_step_multiplicator->SetValue(m_ramming_step_multiplicator);
m_widget_ramming_step_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
m_widget_ramming_line_width_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); });
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(sizer_chart, 0, wxALL, 5);
sizer->Add(sizer_param, 0, wxALL, 10);
sizer->SetSizeHints(this);
SetSizer(sizer);
m_widget_time->Bind(wxEVT_TEXT,[this](wxCommandEvent&) {m_chart->set_xy_range(m_widget_time->GetValue(),-1);});
m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value
m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value
Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} );
Refresh(this);
}
void RammingPanel::line_parameters_changed() {
m_ramming_line_width_multiplicator = m_widget_ramming_line_width_multiplicator->GetValue();
m_ramming_step_multiplicator = m_widget_ramming_step_multiplicator->GetValue();
}
std::string RammingPanel::get_parameters()
{
std::vector<float> speeds = m_chart->get_ramming_speed(0.25f);
std::vector<std::pair<float,float>> buttons = m_chart->get_buttons();
std::stringstream stream;
stream << m_ramming_line_width_multiplicator << " " << m_ramming_step_multiplicator;
for (const float& speed_value : speeds)
stream << " " << speed_value;
stream << "|";
for (const auto& button : buttons)
stream << " " << button.first << " " << button.second;
return stream.str();
}
#define ITEM_WIDTH 60
// Parent dialog for purging volume adjustments - it fathers WipingPanel widget (that contains all controls) and a button to toggle simple/advanced mode:
WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders)
: wxDialog(parent, wxID_ANY, _(L("Wipe tower - Purging volume adjustment")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/)
{
auto widget_button = new wxButton(this,wxID_ANY,"-",wxPoint(0,0),wxDefaultSize);
m_panel_wiping = new WipingPanel(this,matrix,extruders, widget_button);
auto main_sizer = new wxBoxSizer(wxVERTICAL);
// set min sizer width according to extruders count
const auto sizer_width = (int)((sqrt(matrix.size()) + 2.8)*ITEM_WIDTH);
main_sizer->SetMinSize(wxSize(sizer_width, -1));
main_sizer->Add(m_panel_wiping, 0, wxEXPAND | wxALL, 5);
main_sizer->Add(widget_button, 0, wxALIGN_CENTER_HORIZONTAL | wxCENTER | wxBOTTOM, 5);
main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
SetSizer(main_sizer);
main_sizer->SetSizeHints(this);
this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); });
this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) { // if OK button is clicked..
m_output_matrix = m_panel_wiping->read_matrix_values(); // ..query wiping panel and save returned values
m_output_extruders = m_panel_wiping->read_extruders_values(); // so they can be recovered later by calling get_...()
EndModal(wxID_OK);
},wxID_OK);
this->Show();
}
// This function allows to "play" with sizers parameters (like align or border)
void WipingPanel::format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_sizer, const wxString& info, const wxString& table_title, int table_lshift/*=0*/)
{
sizer->Add(new wxStaticText(page, wxID_ANY, info,wxDefaultPosition,wxSize(0,50)), 0, wxEXPAND | wxLEFT, 15);
auto table_sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(table_sizer, 0, wxALIGN_CENTER | wxCENTER, table_lshift);
table_sizer->Add(new wxStaticText(page, wxID_ANY, table_title), 0, wxALIGN_CENTER | wxTOP, 50);
table_sizer->Add(grid_sizer, 0, wxALIGN_CENTER | wxTOP, 10);
}
// This panel contains all control widgets for both simple and advanced mode (these reside in separate sizers)
WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button)
: wxPanel(parent,wxID_ANY, wxDefaultPosition, wxDefaultSize/*,wxBORDER_RAISED*/)
{
m_widget_button = widget_button; // pointer to the button in parent dialog
m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){ toggle_advanced(true); });
m_number_of_extruders = (int)(sqrt(matrix.size())+0.001);
// Create two switched panels with their own sizers
m_sizer_simple = new wxBoxSizer(wxVERTICAL);
m_sizer_advanced = new wxBoxSizer(wxVERTICAL);
m_page_simple = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
m_page_advanced = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
m_page_simple->SetSizer(m_sizer_simple);
m_page_advanced->SetSizer(m_sizer_advanced);
auto gridsizer_simple = new wxGridSizer(3, 5, 10);
m_gridsizer_advanced = new wxGridSizer(m_number_of_extruders+1, 5, 1);
// First create controls for advanced mode and assign them to m_page_advanced:
for (unsigned int i = 0; i < m_number_of_extruders; ++i) {
edit_boxes.push_back(std::vector<wxTextCtrl*>(0));
for (unsigned int j = 0; j < m_number_of_extruders; ++j) {
edit_boxes.back().push_back(new wxTextCtrl(m_page_advanced, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(ITEM_WIDTH, -1)));
if (i == j)
edit_boxes[i][j]->Disable();
else
edit_boxes[i][j]->SetValue(wxString("") << int(matrix[m_number_of_extruders*j + i]));
}
}
m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("")));
for (unsigned int i = 0; i < m_number_of_extruders; ++i)
m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
for (unsigned int i = 0; i < m_number_of_extruders; ++i) {
m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
for (unsigned int j = 0; j < m_number_of_extruders; ++j)
m_gridsizer_advanced->Add(edit_boxes[j][i], 0);
}
// collect and format sizer
format_sizer(m_sizer_advanced, m_page_advanced, m_gridsizer_advanced,
_(L("Here you can adjust required purging volume (mm\u00B3) for any given pair of tools.")),
_(L("Extruder changed to")));
// Hide preview page before new page creating
// It allows to do that from a beginning of the main panel
m_page_advanced->Hide();
// Now the same for simple mode:
gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString("")), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("unloaded")))), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
gridsizer_simple->Add(new wxStaticText(m_page_simple,wxID_ANY,wxString(_(L("loaded")))), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL);
for (unsigned int i=0;i<m_number_of_extruders;++i) {
m_old.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(80, -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i]));
m_new.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(80, -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i+1]));
gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("Tool #"))) << i + 1 << ": "), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
gridsizer_simple->Add(m_old.back(),0);
gridsizer_simple->Add(m_new.back(),0);
}
// collect and format sizer
format_sizer(m_sizer_simple, m_page_simple, gridsizer_simple,
_(L("Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded.")),
_(L("Volume to purge (mm\u00B3) when the filament is being")), 50);
m_sizer = new wxBoxSizer(wxVERTICAL);
m_sizer->Add(m_page_simple, 0, wxEXPAND | wxALL, 25);
m_sizer->Add(m_page_advanced, 0, wxEXPAND | wxALL, 25);
m_sizer->SetSizeHints(this);
SetSizer(m_sizer);
toggle_advanced(); // to show/hide what is appropriate
m_page_advanced->Bind(wxEVT_PAINT,[this](wxPaintEvent&) {
wxPaintDC dc(m_page_advanced);
int y_pos = 0.5 * (edit_boxes[0][0]->GetPosition().y + edit_boxes[0][edit_boxes.size()-1]->GetPosition().y + edit_boxes[0][edit_boxes.size()-1]->GetSize().y);
wxString label = _(L("From"));
int text_width = 0;
int text_height = 0;
dc.GetTextExtent(label,&text_width,&text_height);
int xpos = m_gridsizer_advanced->GetPosition().x;
dc.DrawRotatedText(label,xpos-text_height,y_pos + text_width/2.f,90);
});
}
// Reads values from the (advanced) wiping matrix:
std::vector<float> WipingPanel::read_matrix_values() {
if (!m_advanced)
fill_in_matrix();
std::vector<float> output;
for (unsigned int i=0;i<m_number_of_extruders;++i) {
for (unsigned int j=0;j<m_number_of_extruders;++j) {
double val = 0.;
edit_boxes[j][i]->GetValue().ToDouble(&val);
output.push_back((float)val);
}
}
return output;
}
// Reads values from simple mode to save them for next time:
std::vector<float> WipingPanel::read_extruders_values() {
std::vector<float> output;
for (unsigned int i=0;i<m_number_of_extruders;++i) {
output.push_back(m_old[i]->GetValue());
output.push_back(m_new[i]->GetValue());
}
return output;
}
// This updates the "advanced" matrix based on values from "simple" mode
void WipingPanel::fill_in_matrix() {
for (unsigned i=0;i<m_number_of_extruders;++i) {
for (unsigned j=0;j<m_number_of_extruders;++j) {
if (i==j) continue;
edit_boxes[j][i]->SetValue(wxString("")<< (m_old[i]->GetValue() + m_new[j]->GetValue()));
}
}
}
// Function to check if simple and advanced settings are matching
bool WipingPanel::advanced_matches_simple() {
for (unsigned i=0;i<m_number_of_extruders;++i) {
for (unsigned j=0;j<m_number_of_extruders;++j) {
if (i==j) continue;
if (edit_boxes[j][i]->GetValue() != (wxString("")<< (m_old[i]->GetValue() + m_new[j]->GetValue())))
return false;
}
}
return true;
}
// Switches the dialog from simple to advanced mode and vice versa
void WipingPanel::toggle_advanced(bool user_action) {
if (m_advanced && !advanced_matches_simple() && user_action) {
if (wxMessageDialog(this,wxString(_(L("Switching to simple settings will discard changes done in the advanced mode!\n\nDo you want to proceed?"))),
wxString(_(L("Warning"))),wxYES_NO|wxICON_EXCLAMATION).ShowModal() != wxID_YES)
return;
}
if (user_action)
m_advanced = !m_advanced; // user demands a change -> toggle
else
m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate
(m_advanced ? m_page_advanced : m_page_simple)->Show();
(!m_advanced ? m_page_advanced : m_page_simple)->Hide();
m_widget_button->SetLabel(m_advanced ? _(L("Show simplified settings")) : _(L("Show advanced settings")));
if (m_advanced)
if (user_action) fill_in_matrix(); // otherwise keep values loaded from config
m_sizer->Layout();
Refresh();
}

View file

@ -0,0 +1,90 @@
#ifndef _WIPE_TOWER_DIALOG_H_
#define _WIPE_TOWER_DIALOG_H_
#include <wx/spinctrl.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/checkbox.h>
#include <wx/msgdlg.h>
#include "RammingChart.hpp"
class RammingPanel : public wxPanel {
public:
RammingPanel(wxWindow* parent);
RammingPanel(wxWindow* parent,const std::string& data);
std::string get_parameters();
private:
Chart* m_chart = nullptr;
wxSpinCtrl* m_widget_volume = nullptr;
wxSpinCtrl* m_widget_ramming_line_width_multiplicator = nullptr;
wxSpinCtrl* m_widget_ramming_step_multiplicator = nullptr;
wxSpinCtrlDouble* m_widget_time = nullptr;
int m_ramming_step_multiplicator;
int m_ramming_line_width_multiplicator;
void line_parameters_changed();
};
class RammingDialog : public wxDialog {
public:
RammingDialog(wxWindow* parent,const std::string& parameters);
std::string get_parameters() { return m_output_data; }
private:
RammingPanel* m_panel_ramming = nullptr;
std::string m_output_data;
};
class WipingPanel : public wxPanel {
public:
WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button);
std::vector<float> read_matrix_values();
std::vector<float> read_extruders_values();
void toggle_advanced(bool user_action = false);
void format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_sizer, const wxString& info, const wxString& table_title, int table_lshift=0);
private:
void fill_in_matrix();
bool advanced_matches_simple();
std::vector<wxSpinCtrl*> m_old;
std::vector<wxSpinCtrl*> m_new;
std::vector<std::vector<wxTextCtrl*>> edit_boxes;
unsigned int m_number_of_extruders = 0;
bool m_advanced = false;
wxPanel* m_page_simple = nullptr;
wxPanel* m_page_advanced = nullptr;
wxBoxSizer* m_sizer = nullptr;
wxBoxSizer* m_sizer_simple = nullptr;
wxBoxSizer* m_sizer_advanced = nullptr;
wxGridSizer* m_gridsizer_advanced = nullptr;
wxButton* m_widget_button = nullptr;
};
class WipingDialog : public wxDialog {
public:
WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders);
std::vector<float> get_matrix() const { return m_output_matrix; }
std::vector<float> get_extruders() const { return m_output_extruders; }
private:
WipingPanel* m_panel_wiping = nullptr;
std::vector<float> m_output_matrix;
std::vector<float> m_output_extruders;
};
#endif // _WIPE_TOWER_DIALOG_H_

View file

@ -109,3 +109,59 @@ void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt)
ProcessEvent(event);
}
}
// *** wxDataViewTreeCtrlComboPopup ***
const unsigned int wxDataViewTreeCtrlComboPopup::DefaultWidth = 270;
const unsigned int wxDataViewTreeCtrlComboPopup::DefaultHeight = 200;
const unsigned int wxDataViewTreeCtrlComboPopup::DefaultItemHeight = 22;
bool wxDataViewTreeCtrlComboPopup::Create(wxWindow* parent)
{
return wxDataViewTreeCtrl::Create(parent, wxID_ANY/*HIGHEST + 1*/, wxPoint(0, 0), wxDefaultSize/*wxSize(270, -1)*/, wxDV_NO_HEADER);
}
/*
wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
{
// matches owner wxComboCtrl's width
// and sets height dinamically in dependence of contained items count
wxComboCtrl* cmb = GetComboCtrl();
if (cmb != nullptr)
{
wxSize size = GetComboCtrl()->GetSize();
if (m_cnt_open_items > 0)
size.SetHeight(m_cnt_open_items * DefaultItemHeight);
else
size.SetHeight(DefaultHeight);
return size;
}
else
return wxSize(DefaultWidth, DefaultHeight);
}
*/
void wxDataViewTreeCtrlComboPopup::OnKeyEvent(wxKeyEvent& evt)
{
// filters out all the keys which are not working properly
if (evt.GetKeyCode() == WXK_UP)
{
return;
}
else if (evt.GetKeyCode() == WXK_DOWN)
{
return;
}
else
{
evt.Skip();
return;
}
}
void wxDataViewTreeCtrlComboPopup::OnDataViewTreeCtrlSelection(wxCommandEvent& evt)
{
wxComboCtrl* cmb = GetComboCtrl();
auto selected = GetItemText(GetSelection());
cmb->SetText(selected);
}

View file

@ -3,6 +3,7 @@
#include <wx/checklst.h>
#include <wx/combo.h>
#include <wx/dataview.h>
class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
{
@ -25,4 +26,28 @@ public:
void OnListBoxSelection(wxCommandEvent& evt);
};
// *** wxDataViewTreeCtrlComboBox ***
class wxDataViewTreeCtrlComboPopup: public wxDataViewTreeCtrl, public wxComboPopup
{
static const unsigned int DefaultWidth;
static const unsigned int DefaultHeight;
static const unsigned int DefaultItemHeight;
wxString m_text;
int m_cnt_open_items{0};
public:
virtual bool Create(wxWindow* parent);
virtual wxWindow* GetControl() { return this; }
virtual void SetStringValue(const wxString& value) { m_text = value; }
virtual wxString GetStringValue() const { return m_text; }
// virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight);
virtual void OnKeyEvent(wxKeyEvent& evt);
void OnDataViewTreeCtrlSelection(wxCommandEvent& evt);
void SetItemsCnt(int cnt) { m_cnt_open_items = cnt; }
};
#endif // slic3r_GUI_wxExtensions_hpp_

View file

@ -84,7 +84,7 @@
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);
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);
void erase()
%code{% THIS->clear(); %};
@ -105,6 +105,7 @@
void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z);
void update_outside_state(DynamicPrintConfig* config, bool all_inside);
void update_colors_by_extruder(DynamicPrintConfig* config);
bool move_volume_up(int idx)
%code%{

View file

@ -142,6 +142,8 @@ PresetCollection::arrayref()
Ref<PresetCollection> print() %code%{ RETVAL = &THIS->prints; %};
Ref<PresetCollection> filament() %code%{ RETVAL = &THIS->filaments; %};
Ref<PresetCollection> printer() %code%{ RETVAL = &THIS->printers; %};
Ref<DynamicPrintConfig> project_config() %code%{ RETVAL = &THIS->project_config; %};
bool has_defauls_only();
std::vector<std::string> filament_presets() %code%{ RETVAL = THIS->filament_presets; %};