Merged with dev
This commit is contained in:
parent
d934b63424
commit
fe3b92870f
@ -334,28 +334,4 @@ sub set_menu_item_icon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub save_window_pos {
|
|
||||||
my ($self, $window, $name) = @_;
|
|
||||||
|
|
||||||
$self->{app_config}->set("${name}_pos", join ',', $window->GetScreenPositionXY);
|
|
||||||
$self->{app_config}->set("${name}_size", join ',', $window->GetSizeWH);
|
|
||||||
$self->{app_config}->set("${name}_maximized", $window->IsMaximized);
|
|
||||||
$self->{app_config}->save;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub restore_window_pos {
|
|
||||||
my ($self, $window, $name) = @_;
|
|
||||||
if ($self->{app_config}->has("${name}_pos")) {
|
|
||||||
my $size = [ split ',', $self->{app_config}->get("${name}_size"), 2 ];
|
|
||||||
$window->SetSize($size);
|
|
||||||
|
|
||||||
my $display = Wx::Display->new->GetClientArea();
|
|
||||||
my $pos = [ split ',', $self->{app_config}->get("${name}_pos"), 2 ];
|
|
||||||
if (($pos->[0] + $size->[0]/2) < $display->GetRight && ($pos->[1] + $size->[1]/2) < $display->GetBottom) {
|
|
||||||
$window->Move($pos);
|
|
||||||
}
|
|
||||||
$window->Maximize(1) if $self->{app_config}->get("${name}_maximized");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
@ -95,7 +95,7 @@ sub new {
|
|||||||
$self->Fit;
|
$self->Fit;
|
||||||
$self->SetMinSize([760, 490]);
|
$self->SetMinSize([760, 490]);
|
||||||
$self->SetSize($self->GetMinSize);
|
$self->SetSize($self->GetMinSize);
|
||||||
wxTheApp->restore_window_pos($self, "main_frame");
|
Slic3r::GUI::restore_window_size($self, "main_frame");
|
||||||
$self->Show;
|
$self->Show;
|
||||||
$self->Layout;
|
$self->Layout;
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ sub new {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
# save window size
|
# save window size
|
||||||
wxTheApp->save_window_pos($self, "main_frame");
|
Slic3r::GUI::save_window_size($self, "main_frame");
|
||||||
# Save the slic3r.ini. Usually the ini file is saved from "on idle" callback,
|
# Save the slic3r.ini. Usually the ini file is saved from "on idle" callback,
|
||||||
# but in rare cases it may not have been called yet.
|
# but in rare cases it may not have been called yet.
|
||||||
wxTheApp->{app_config}->save;
|
wxTheApp->{app_config}->save;
|
||||||
@ -207,10 +207,12 @@ sub _init_tabpanel {
|
|||||||
EVT_COMMAND($self, -1, $OBJECT_SELECTION_CHANGED_EVENT, sub {
|
EVT_COMMAND($self, -1, $OBJECT_SELECTION_CHANGED_EVENT, sub {
|
||||||
my ($self, $event) = @_;
|
my ($self, $event) = @_;
|
||||||
my $obj_idx = $event->GetId;
|
my $obj_idx = $event->GetId;
|
||||||
my $child = $event->GetInt == 1 ? 1 : undef;
|
# my $child = $event->GetInt == 1 ? 1 : undef;
|
||||||
|
# $self->{plater}->select_object($obj_idx < 0 ? undef: $obj_idx, $child);
|
||||||
|
# $self->{plater}->item_changed_selection($obj_idx);
|
||||||
|
|
||||||
$self->{plater}->select_object($obj_idx < 0 ? undef: $obj_idx, $child);
|
my $vol_idx = $event->GetInt;
|
||||||
$self->{plater}->item_changed_selection($obj_idx);
|
$self->{plater}->select_object_from_cpp($obj_idx < 0 ? undef: $obj_idx, $vol_idx<0 ? -1 : $vol_idx);
|
||||||
});
|
});
|
||||||
|
|
||||||
# The following event is emited by the C++ GUI implementation on object settings change.
|
# The following event is emited by the C++ GUI implementation on object settings change.
|
||||||
|
@ -104,10 +104,11 @@ sub new {
|
|||||||
# callback to enable/disable action buttons
|
# callback to enable/disable action buttons
|
||||||
my $enable_action_buttons = sub {
|
my $enable_action_buttons = sub {
|
||||||
my ($enable) = @_;
|
my ($enable) = @_;
|
||||||
$self->{btn_export_gcode}->Enable($enable);
|
Slic3r::GUI::enable_action_buttons($enable);
|
||||||
$self->{btn_reslice}->Enable($enable);
|
# $self->{btn_export_gcode}->Enable($enable);
|
||||||
$self->{btn_print}->Enable($enable);
|
# $self->{btn_reslice}->Enable($enable);
|
||||||
$self->{btn_send_gcode}->Enable($enable);
|
# $self->{btn_print}->Enable($enable);
|
||||||
|
# $self->{btn_send_gcode}->Enable($enable);
|
||||||
};
|
};
|
||||||
|
|
||||||
# callback to react to gizmo scale
|
# callback to react to gizmo scale
|
||||||
@ -230,7 +231,14 @@ sub new {
|
|||||||
my ($obj_idx, $object) = $self->selected_object;
|
my ($obj_idx, $object) = $self->selected_object;
|
||||||
if (defined $obj_idx) {
|
if (defined $obj_idx) {
|
||||||
my $vol_idx = Slic3r::GUI::_3DScene::get_first_volume_id($self->{canvas3D}, $obj_idx);
|
my $vol_idx = Slic3r::GUI::_3DScene::get_first_volume_id($self->{canvas3D}, $obj_idx);
|
||||||
Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx) if ($vol_idx != -1);
|
#Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx) if ($vol_idx != -1);
|
||||||
|
my $inst_cnt = $self->{model}->objects->[$obj_idx]->instances_count;
|
||||||
|
for (0..$inst_cnt-1){
|
||||||
|
Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $_ + $vol_idx) if ($vol_idx != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $volume_idx = Slic3r::GUI::_3DScene::get_in_object_volume_id($self->{canvas3D}, $vol_idx);
|
||||||
|
Slic3r::GUI::select_current_volume($obj_idx, $volume_idx) if ($volume_idx != -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -368,26 +376,35 @@ sub new {
|
|||||||
# }
|
# }
|
||||||
|
|
||||||
### Panel for right column
|
### Panel for right column
|
||||||
# $self->{right_panel} = Wx::Panel->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
$self->{right_panel} = Wx::Panel->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||||
$self->{right_panel} = Wx::ScrolledWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
# $self->{right_panel} = Wx::ScrolledWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||||
$self->{right_panel}->SetScrollbars(0, 1, 1, 1);
|
# $self->{right_panel}->SetScrollbars(0, 1, 1, 1);
|
||||||
|
|
||||||
|
### Scrolled Window for panel without "Export G-code" and "Slice now" buttons
|
||||||
|
my $scrolled_window_sizer = $self->{scrolled_window_sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
|
$scrolled_window_sizer->SetMinSize([320, -1]);
|
||||||
|
my $scrolled_window_panel = $self->{scrolled_window_panel} = Wx::ScrolledWindow->new($self->{right_panel}, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||||
|
$scrolled_window_panel->SetSizer($scrolled_window_sizer);
|
||||||
|
$scrolled_window_panel->SetScrollbars(0, 1, 1, 1);
|
||||||
|
|
||||||
# right pane buttons
|
# right pane buttons
|
||||||
$self->{btn_export_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Export G-code…"), wxDefaultPosition, [-1, 30], wxNO_BORDER);#, wxBU_LEFT);
|
$self->{btn_export_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Export G-code…"), wxDefaultPosition, [-1, 30],);# wxNO_BORDER);#, wxBU_LEFT);
|
||||||
$self->{btn_reslice} = Wx::Button->new($self->{right_panel}, -1, L("Slice now"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
$self->{btn_reslice} = Wx::Button->new($self->{right_panel}, -1, L("Slice now"), wxDefaultPosition, [-1, 30]);#, wxNO_BORDER);#, wxBU_LEFT);
|
||||||
$self->{btn_print} = Wx::Button->new($self->{right_panel}, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
# $self->{btn_print} = Wx::Button->new($self->{right_panel}, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||||
$self->{btn_send_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
# $self->{btn_send_gcode} = Wx::Button->new($self->{right_panel}, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||||
$self->{btn_export_stl} = Wx::Button->new($self->{right_panel}, -1, L("Export STL…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
$self->{btn_print} = Wx::Button->new($scrolled_window_panel, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||||
|
$self->{btn_send_gcode} = Wx::Button->new($scrolled_window_panel, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||||
|
#$self->{btn_export_stl} = Wx::Button->new($self->{right_panel}, -1, L("Export STL…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||||
#$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font);
|
#$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font);
|
||||||
#$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font);
|
#$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font);
|
||||||
$self->{btn_print}->Hide;
|
$self->{btn_print}->Hide;
|
||||||
$self->{btn_send_gcode}->Hide;
|
$self->{btn_send_gcode}->Hide;
|
||||||
|
|
||||||
# export_gcode cog_go.png
|
# export_gcode cog_go.png
|
||||||
|
#! reslice reslice.png
|
||||||
my %icons = qw(
|
my %icons = qw(
|
||||||
print arrow_up.png
|
print arrow_up.png
|
||||||
send_gcode arrow_up.png
|
send_gcode arrow_up.png
|
||||||
reslice reslice.png
|
|
||||||
export_stl brick_go.png
|
export_stl brick_go.png
|
||||||
);
|
);
|
||||||
for (grep $self->{"btn_$_"}, keys %icons) {
|
for (grep $self->{"btn_$_"}, keys %icons) {
|
||||||
@ -494,9 +511,11 @@ sub new {
|
|||||||
# $self->{preset_choosers}{$group}[$idx]
|
# $self->{preset_choosers}{$group}[$idx]
|
||||||
$self->{preset_choosers} = {};
|
$self->{preset_choosers} = {};
|
||||||
for my $group (qw(print filament sla_material printer)) {
|
for my $group (qw(print filament sla_material printer)) {
|
||||||
my $text = Wx::StaticText->new($self->{right_panel}, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
|
# my $text = Wx::StaticText->new($self->{right_panel}, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
|
||||||
|
my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$group_labels{$group}:", wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
|
||||||
$text->SetFont($Slic3r::GUI::small_font);
|
$text->SetFont($Slic3r::GUI::small_font);
|
||||||
my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY);
|
# my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY);
|
||||||
|
my $choice = Wx::BitmapComboBox->new($scrolled_window_panel, -1, "", wxDefaultPosition, wxDefaultSize, [], wxCB_READONLY);
|
||||||
if ($group eq 'filament') {
|
if ($group eq 'filament') {
|
||||||
EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down(0, @_); } );
|
EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down(0, @_); } );
|
||||||
}
|
}
|
||||||
@ -515,26 +534,13 @@ sub new {
|
|||||||
}
|
}
|
||||||
|
|
||||||
my $frequently_changed_parameters_sizer = $self->{frequently_changed_parameters_sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
my $frequently_changed_parameters_sizer = $self->{frequently_changed_parameters_sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
Slic3r::GUI::add_frequently_changed_parameters($self->{right_panel}, $frequently_changed_parameters_sizer, $presets);
|
#! Slic3r::GUI::add_frequently_changed_parameters($self->{right_panel}, $frequently_changed_parameters_sizer, $presets);
|
||||||
|
Slic3r::GUI::add_frequently_changed_parameters($self->{scrolled_window_panel}, $frequently_changed_parameters_sizer, $presets);
|
||||||
my $expert_mode_part_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
|
||||||
Slic3r::GUI::add_expert_mode_part( $self->{right_panel}, $expert_mode_part_sizer,
|
|
||||||
$self->{model},
|
|
||||||
$self->{event_object_selection_changed},
|
|
||||||
$self->{event_object_settings_changed},
|
|
||||||
$self->{event_remove_object},
|
|
||||||
$self->{event_update_scene});
|
|
||||||
# if ($expert_mode_part_sizer->IsShown(2)==1)
|
|
||||||
# {
|
|
||||||
# $expert_mode_part_sizer->Layout;
|
|
||||||
# $expert_mode_part_sizer->Show(2, 0); # ? Why doesn't work
|
|
||||||
# $self->{right_panel}->Layout;
|
|
||||||
# }
|
|
||||||
|
|
||||||
my $object_info_sizer;
|
my $object_info_sizer;
|
||||||
{
|
{
|
||||||
# my $box = Wx::StaticBox->new($scrolled_window_panel, -1, L("Info"));
|
my $box = Wx::StaticBox->new($scrolled_window_panel, -1, L("Info"));
|
||||||
my $box = Wx::StaticBox->new($self->{right_panel}, -1, L("Info"));
|
# my $box = Wx::StaticBox->new($self->{right_panel}, -1, L("Info"));
|
||||||
$box->SetFont($Slic3r::GUI::small_bold_font);
|
$box->SetFont($Slic3r::GUI::small_bold_font);
|
||||||
$object_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
|
$object_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
|
||||||
$object_info_sizer->SetMinSize([300,-1]);
|
$object_info_sizer->SetMinSize([300,-1]);
|
||||||
@ -554,25 +560,26 @@ sub new {
|
|||||||
);
|
);
|
||||||
while (my $field = shift @info) {
|
while (my $field = shift @info) {
|
||||||
my $label = shift @info;
|
my $label = shift @info;
|
||||||
# my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
my $text = Wx::StaticText->new($scrolled_window_panel, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||||
my $text = Wx::StaticText->new($self->{right_panel}, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
# my $text = Wx::StaticText->new($self->{right_panel}, -1, "$label:", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||||
$text->SetFont($Slic3r::GUI::small_font);
|
$text->SetFont($Slic3r::GUI::small_font);
|
||||||
#!$grid_sizer->Add($text, 0);
|
#!$grid_sizer->Add($text, 0);
|
||||||
|
|
||||||
# $self->{"object_info_$field"} = Wx::StaticText->new($scrolled_window_panel, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
$self->{"object_info_$field"} = Wx::StaticText->new($scrolled_window_panel, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||||
$self->{"object_info_$field"} = Wx::StaticText->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
# $self->{"object_info_$field"} = Wx::StaticText->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||||
$self->{"object_info_$field"}->SetFont($Slic3r::GUI::small_font);
|
$self->{"object_info_$field"}->SetFont($Slic3r::GUI::small_font);
|
||||||
if ($field eq 'manifold') {
|
if ($field eq 'manifold') {
|
||||||
# $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($scrolled_window_panel, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG));
|
$self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($scrolled_window_panel, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG));
|
||||||
$self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self->{right_panel}, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG));
|
# $self->{object_info_manifold_warning_icon} = Wx::StaticBitmap->new($self->{right_panel}, -1, Wx::Bitmap->new(Slic3r::var("error.png"), wxBITMAP_TYPE_PNG));
|
||||||
#$self->{object_info_manifold_warning_icon}->Hide;
|
#$self->{object_info_manifold_warning_icon}->Hide;
|
||||||
$self->{"object_info_manifold_warning_icon_show"} = sub {
|
$self->{"object_info_manifold_warning_icon_show"} = sub {
|
||||||
if ($self->{object_info_manifold_warning_icon}->IsShown() != $_[0]) {
|
if ($self->{object_info_manifold_warning_icon}->IsShown() != $_[0]) {
|
||||||
|
# this fuction show/hide info_manifold_warning_icon on the c++ side now
|
||||||
Slic3r::GUI::set_show_manifold_warning_icon($_[0]);
|
Slic3r::GUI::set_show_manifold_warning_icon($_[0]);
|
||||||
my $mode = wxTheApp->{app_config}->get("view_mode");
|
#my $mode = wxTheApp->{app_config}->get("view_mode");
|
||||||
return if ($mode eq "" || $mode eq "simple");
|
#return if ($mode eq "" || $mode eq "simple");
|
||||||
$self->{object_info_manifold_warning_icon}->Show($_[0]);
|
#$self->{object_info_manifold_warning_icon}->Show($_[0]);
|
||||||
$self->Layout
|
#$self->Layout
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
$self->{"object_info_manifold_warning_icon_show"}->(0);
|
$self->{"object_info_manifold_warning_icon_show"}->(0);
|
||||||
@ -590,18 +597,19 @@ sub new {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my $print_info_sizer = $self->{print_info_sizer} = Wx::StaticBoxSizer->new(
|
my $print_info_box = Wx::StaticBox->new($scrolled_window_panel, -1, L("Sliced Info"));
|
||||||
# Wx::StaticBox->new($scrolled_window_panel, -1, L("Sliced Info")), wxVERTICAL);
|
$print_info_box->SetFont($Slic3r::GUI::small_bold_font);
|
||||||
Wx::StaticBox->new($self->{right_panel}, -1, L("Sliced Info")), wxVERTICAL);
|
my $print_info_sizer = $self->{print_info_sizer} = Wx::StaticBoxSizer->new($print_info_box, wxVERTICAL);
|
||||||
|
# Wx::StaticBox->new($self->{right_panel}, -1, L("Sliced Info")), wxVERTICAL);
|
||||||
$print_info_sizer->SetMinSize([300,-1]);
|
$print_info_sizer->SetMinSize([300,-1]);
|
||||||
|
|
||||||
my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
$self->{buttons_sizer} = $buttons_sizer;
|
$self->{buttons_sizer} = $buttons_sizer;
|
||||||
$buttons_sizer->AddStretchSpacer(1);
|
$buttons_sizer->AddStretchSpacer(1);
|
||||||
$buttons_sizer->Add($self->{btn_export_stl}, 0, wxALIGN_RIGHT, 0);
|
# $buttons_sizer->Add($self->{btn_export_stl}, 0, wxALIGN_RIGHT, 0);
|
||||||
$buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0);
|
#! $buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0);
|
||||||
$buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT, 0);
|
$buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT | wxBOTTOM | wxTOP, 5);
|
||||||
$buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT, 0);
|
$buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT | wxBOTTOM | wxTOP, 5);
|
||||||
|
|
||||||
# $scrolled_window_sizer->Add($self->{list}, 1, wxEXPAND, 5);
|
# $scrolled_window_sizer->Add($self->{list}, 1, wxEXPAND, 5);
|
||||||
# $scrolled_window_sizer->Add($object_info_sizer, 0, wxEXPAND, 0);
|
# $scrolled_window_sizer->Add($object_info_sizer, 0, wxEXPAND, 0);
|
||||||
@ -611,24 +619,39 @@ sub new {
|
|||||||
### Sizer for info boxes
|
### Sizer for info boxes
|
||||||
my $info_sizer = $self->{info_sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
my $info_sizer = $self->{info_sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
$info_sizer->SetMinSize([318, -1]);
|
$info_sizer->SetMinSize([318, -1]);
|
||||||
$info_sizer->Add($object_info_sizer, 0, wxEXPAND | wxBOTTOM, 5);
|
$info_sizer->Add($object_info_sizer, 0, wxEXPAND | wxTOP, 20);
|
||||||
$info_sizer->Add($print_info_sizer, 0, wxEXPAND | wxBOTTOM, 5);
|
$info_sizer->Add($print_info_sizer, 0, wxEXPAND | wxTOP, 20);
|
||||||
|
|
||||||
|
$scrolled_window_sizer->Add($presets, 0, wxEXPAND | wxLEFT, 2) if defined $presets;
|
||||||
|
$scrolled_window_sizer->Add($frequently_changed_parameters_sizer, 1, wxEXPAND | wxLEFT, 0) if defined $frequently_changed_parameters_sizer;
|
||||||
|
$scrolled_window_sizer->Add($buttons_sizer, 0, wxEXPAND, 0);
|
||||||
|
$scrolled_window_sizer->Add($info_sizer, 0, wxEXPAND | wxLEFT, 20);
|
||||||
|
# Show the box initially, let it be shown after the slicing is finished.
|
||||||
|
$self->print_info_box_show(0);
|
||||||
|
|
||||||
|
### Sizer for "Export G-code" & "Slice now" buttons
|
||||||
|
my $btns_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
|
$btns_sizer->SetMinSize([318, -1]);
|
||||||
|
$btns_sizer->Add($self->{btn_reslice}, 0, wxEXPAND, 0);
|
||||||
|
$btns_sizer->Add($self->{btn_export_gcode}, 0, wxEXPAND | wxTOP, 5);
|
||||||
|
|
||||||
my $right_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
my $right_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
$self->{right_panel}->SetSizer($right_sizer);
|
$self->{right_panel}->SetSizer($right_sizer);
|
||||||
$right_sizer->SetMinSize([320, -1]);
|
$right_sizer->SetMinSize([320, -1]);
|
||||||
$right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets;
|
#! $right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets;
|
||||||
$right_sizer->Add($frequently_changed_parameters_sizer, 1, wxEXPAND | wxTOP, 0) if defined $frequently_changed_parameters_sizer;
|
#! $right_sizer->Add($frequently_changed_parameters_sizer, 1, wxEXPAND | wxTOP, 0) if defined $frequently_changed_parameters_sizer;
|
||||||
$right_sizer->Add($expert_mode_part_sizer, 0, wxEXPAND | wxTOP, 10) if defined $expert_mode_part_sizer;
|
#! $right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM | wxTOP, 10);
|
||||||
$right_sizer->Add($buttons_sizer, 0, wxEXPAND | wxBOTTOM | wxTOP, 10);
|
#! $right_sizer->Add($info_sizer, 0, wxEXPAND | wxLEFT, 20);
|
||||||
$right_sizer->Add($info_sizer, 0, wxEXPAND | wxLEFT, 20);
|
|
||||||
# Show the box initially, let it be shown after the slicing is finished.
|
# Show the box initially, let it be shown after the slicing is finished.
|
||||||
$self->print_info_box_show(0);
|
#! $self->print_info_box_show(0);
|
||||||
$right_sizer->Add($self->{btn_export_gcode}, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 20);
|
$right_sizer->Add($scrolled_window_panel, 1, wxEXPAND | wxTOP, 5);
|
||||||
|
# $right_sizer->Add($self->{btn_reslice}, 0, wxEXPAND | wxLEFT | wxTOP, 20);
|
||||||
|
# $right_sizer->Add($self->{btn_export_gcode}, 0, wxEXPAND | wxLEFT | wxTOP, 20);
|
||||||
|
$right_sizer->Add($btns_sizer, 0, wxEXPAND | wxLEFT | wxTOP, 20);
|
||||||
|
|
||||||
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
$hsizer->Add($self->{preview_notebook}, 1, wxEXPAND | wxTOP, 1);
|
$hsizer->Add($self->{preview_notebook}, 1, wxEXPAND | wxTOP, 1);
|
||||||
$hsizer->Add($self->{right_panel}, 0, wxEXPAND | wxLEFT | wxRIGHT, 3);
|
$hsizer->Add($self->{right_panel}, 0, wxEXPAND | wxLEFT | wxRIGHT, 0);#3);
|
||||||
|
|
||||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
# $sizer->Add($self->{htoolbar}, 0, wxEXPAND, 0) if $self->{htoolbar};
|
# $sizer->Add($self->{htoolbar}, 0, wxEXPAND, 0) if $self->{htoolbar};
|
||||||
@ -639,16 +662,21 @@ sub new {
|
|||||||
$self->SetSizer($sizer);
|
$self->SetSizer($sizer);
|
||||||
|
|
||||||
# Send sizers/buttons to C++
|
# Send sizers/buttons to C++
|
||||||
Slic3r::GUI::set_objects_from_perl( $self->{right_panel},
|
Slic3r::GUI::set_objects_from_perl( $self->{scrolled_window_panel},
|
||||||
$frequently_changed_parameters_sizer,
|
$frequently_changed_parameters_sizer,
|
||||||
$expert_mode_part_sizer,
|
$info_sizer,
|
||||||
$info_sizer,
|
$self->{btn_export_gcode},
|
||||||
$self->{btn_export_gcode},
|
# $self->{btn_export_stl},
|
||||||
$self->{btn_export_stl},
|
$self->{btn_reslice},
|
||||||
$self->{btn_reslice},
|
$self->{btn_print},
|
||||||
$self->{btn_print},
|
$self->{btn_send_gcode},
|
||||||
$self->{btn_send_gcode},
|
$self->{object_info_manifold_warning_icon} );
|
||||||
$self->{object_info_manifold_warning_icon} );
|
|
||||||
|
Slic3r::GUI::set_model_events_from_perl( $self->{model},
|
||||||
|
$self->{event_object_selection_changed},
|
||||||
|
$self->{event_object_settings_changed},
|
||||||
|
$self->{event_remove_object},
|
||||||
|
$self->{event_update_scene});
|
||||||
}
|
}
|
||||||
|
|
||||||
# Last correct selected item for each preset
|
# Last correct selected item for each preset
|
||||||
@ -1594,14 +1622,16 @@ sub print_info_box_show {
|
|||||||
# my $scrolled_window_panel = $self->{scrolled_window_panel};
|
# my $scrolled_window_panel = $self->{scrolled_window_panel};
|
||||||
# my $scrolled_window_sizer = $self->{scrolled_window_sizer};
|
# my $scrolled_window_sizer = $self->{scrolled_window_sizer};
|
||||||
# return if (!$show && ($scrolled_window_sizer->IsShown(2) == $show));
|
# return if (!$show && ($scrolled_window_sizer->IsShown(2) == $show));
|
||||||
my $panel = $self->{right_panel};
|
my $panel = $self->{scrolled_window_panel};#$self->{right_panel};
|
||||||
my $sizer = $self->{info_sizer};
|
my $sizer = $self->{info_sizer};
|
||||||
return if (!$sizer || !$show && ($sizer->IsShown(1) == $show));
|
# return if (!$sizer || !$show && ($sizer->IsShown(1) == $show));
|
||||||
|
return if (!$sizer);
|
||||||
|
|
||||||
Slic3r::GUI::set_show_print_info($show);
|
Slic3r::GUI::set_show_print_info($show);
|
||||||
return if (wxTheApp->{app_config}->get("view_mode") eq "simple");
|
# return if (wxTheApp->{app_config}->get("view_mode") eq "simple");
|
||||||
|
|
||||||
if ($show) {
|
# if ($show)
|
||||||
|
{
|
||||||
my $print_info_sizer = $self->{print_info_sizer};
|
my $print_info_sizer = $self->{print_info_sizer};
|
||||||
$print_info_sizer->Clear(1);
|
$print_info_sizer->Clear(1);
|
||||||
my $grid_sizer = Wx::FlexGridSizer->new(2, 2, 5, 5);
|
my $grid_sizer = Wx::FlexGridSizer->new(2, 2, 5, 5);
|
||||||
@ -1609,20 +1639,37 @@ sub print_info_box_show {
|
|||||||
$grid_sizer->AddGrowableCol(1, 1);
|
$grid_sizer->AddGrowableCol(1, 1);
|
||||||
$grid_sizer->AddGrowableCol(3, 1);
|
$grid_sizer->AddGrowableCol(3, 1);
|
||||||
$print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
|
$print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
|
||||||
|
my $is_wipe_tower = $self->{print}->total_wipe_tower_filament > 0;
|
||||||
my @info = (
|
my @info = (
|
||||||
L("Used Filament (m)")
|
L("Used Filament (m)")
|
||||||
=> sprintf("%.2f" , $self->{print}->total_used_filament / 1000),
|
=> $is_wipe_tower ?
|
||||||
|
sprintf("%.2f (%.2f %s + %.2f %s)" , $self->{print}->total_used_filament / 1000,
|
||||||
|
($self->{print}->total_used_filament - $self->{print}->total_wipe_tower_filament) / 1000,
|
||||||
|
L("objects"),
|
||||||
|
$self->{print}->total_wipe_tower_filament / 1000,
|
||||||
|
L("wipe tower")) :
|
||||||
|
sprintf("%.2f" , $self->{print}->total_used_filament / 1000),
|
||||||
|
|
||||||
L("Used Filament (mm³)")
|
L("Used Filament (mm³)")
|
||||||
=> sprintf("%.2f" , $self->{print}->total_extruded_volume),
|
=> sprintf("%.2f" , $self->{print}->total_extruded_volume),
|
||||||
L("Used Filament (g)"),
|
L("Used Filament (g)"),
|
||||||
=> sprintf("%.2f" , $self->{print}->total_weight),
|
=> sprintf("%.2f" , $self->{print}->total_weight),
|
||||||
L("Cost"),
|
L("Cost"),
|
||||||
=> sprintf("%.2f" , $self->{print}->total_cost),
|
=> $is_wipe_tower ?
|
||||||
|
sprintf("%.2f (%.2f %s + %.2f %s)" , $self->{print}->total_cost,
|
||||||
|
($self->{print}->total_cost - $self->{print}->total_wipe_tower_cost),
|
||||||
|
L("objects"),
|
||||||
|
$self->{print}->total_wipe_tower_cost,
|
||||||
|
L("wipe tower")) :
|
||||||
|
sprintf("%.2f" , $self->{print}->total_cost),
|
||||||
L("Estimated printing time (normal mode)")
|
L("Estimated printing time (normal mode)")
|
||||||
=> $self->{print}->estimated_normal_print_time,
|
=> $self->{print}->estimated_normal_print_time,
|
||||||
L("Estimated printing time (silent mode)")
|
L("Estimated printing time (silent mode)")
|
||||||
=> $self->{print}->estimated_silent_print_time
|
=> $self->{print}->estimated_silent_print_time
|
||||||
);
|
);
|
||||||
|
# if there is a wipe tower, insert number of toolchanges info into the array:
|
||||||
|
splice (@info, 8, 0, L("Number of tool changes") => sprintf("%.d", $self->{print}->wipe_tower_number_of_toolchanges)) if ($is_wipe_tower);
|
||||||
|
|
||||||
while ( my $label = shift @info) {
|
while ( my $label = shift @info) {
|
||||||
my $value = shift @info;
|
my $value = shift @info;
|
||||||
next if $value eq "N/A";
|
next if $value eq "N/A";
|
||||||
@ -1639,7 +1686,7 @@ sub print_info_box_show {
|
|||||||
|
|
||||||
# $scrolled_window_sizer->Show(2, $show);
|
# $scrolled_window_sizer->Show(2, $show);
|
||||||
# $scrolled_window_panel->Layout;
|
# $scrolled_window_panel->Layout;
|
||||||
$sizer->Show(1, $show);
|
$sizer->Show(1, $show && wxTheApp->{app_config}->get("view_mode") ne "simple");
|
||||||
|
|
||||||
$self->Layout;
|
$self->Layout;
|
||||||
$panel->Refresh;
|
$panel->Refresh;
|
||||||
@ -1823,6 +1870,7 @@ sub _get_export_file {
|
|||||||
# (i.e. when an object is added/removed/moved/rotated/scaled)
|
# (i.e. when an object is added/removed/moved/rotated/scaled)
|
||||||
sub update {
|
sub update {
|
||||||
my ($self, $force_autocenter) = @_;
|
my ($self, $force_autocenter) = @_;
|
||||||
|
$self->Freeze;
|
||||||
if (wxTheApp->{app_config}->get("autocenter") || $force_autocenter) {
|
if (wxTheApp->{app_config}->get("autocenter") || $force_autocenter) {
|
||||||
$self->{model}->center_instances_around_point($self->bed_centerf);
|
$self->{model}->center_instances_around_point($self->bed_centerf);
|
||||||
}
|
}
|
||||||
@ -1836,6 +1884,7 @@ sub update {
|
|||||||
$self->{preview3D}->reset_gcode_preview_data if $self->{preview3D};
|
$self->{preview3D}->reset_gcode_preview_data if $self->{preview3D};
|
||||||
$self->{preview3D}->reload_print if $self->{preview3D};
|
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||||
$self->schedule_background_process;
|
$self->schedule_background_process;
|
||||||
|
$self->Thaw;
|
||||||
}
|
}
|
||||||
|
|
||||||
# When a printer technology is changed, the UI needs to be updated to show/hide needed preset combo boxes.
|
# When a printer technology is changed, the UI needs to be updated to show/hide needed preset combo boxes.
|
||||||
@ -1868,7 +1917,8 @@ sub on_extruders_change {
|
|||||||
my @presets = $choices->[0]->GetStrings;
|
my @presets = $choices->[0]->GetStrings;
|
||||||
|
|
||||||
# initialize new choice
|
# initialize new choice
|
||||||
my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY);
|
# my $choice = Wx::BitmapComboBox->new($self->{right_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY);
|
||||||
|
my $choice = Wx::BitmapComboBox->new($self->{scrolled_window_panel}, -1, "", wxDefaultPosition, wxDefaultSize, [@presets], wxCB_READONLY);
|
||||||
my $extruder_idx = scalar @$choices;
|
my $extruder_idx = scalar @$choices;
|
||||||
EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down($extruder_idx, @_); } );
|
EVT_LEFT_DOWN($choice, sub { $self->filament_color_box_lmouse_down($extruder_idx, @_); } );
|
||||||
push @$choices, $choice;
|
push @$choices, $choice;
|
||||||
@ -1895,6 +1945,7 @@ sub on_extruders_change {
|
|||||||
$choices->[-1]->Destroy;
|
$choices->[-1]->Destroy;
|
||||||
pop @$choices;
|
pop @$choices;
|
||||||
}
|
}
|
||||||
|
$self->{right_panel}->Layout;
|
||||||
$self->Layout;
|
$self->Layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2221,15 +2272,59 @@ sub select_object {
|
|||||||
if (defined $obj_idx) {
|
if (defined $obj_idx) {
|
||||||
$self->{objects}->[$obj_idx]->selected(1);
|
$self->{objects}->[$obj_idx]->selected(1);
|
||||||
# Select current object in the list on c++ side, if item isn't child
|
# Select current object in the list on c++ side, if item isn't child
|
||||||
if (!defined $child){
|
# if (!defined $child){
|
||||||
Slic3r::GUI::select_current_object($obj_idx);}
|
# Slic3r::GUI::select_current_object($obj_idx);} # all selections in the object list is on c++ side
|
||||||
} else {
|
} else {
|
||||||
# Unselect all objects in the list on c++ side
|
# Unselect all objects in the list on c++ side
|
||||||
Slic3r::GUI::unselect_objects();
|
# Slic3r::GUI::unselect_objects(); # all selections in the object list is on c++ side
|
||||||
}
|
}
|
||||||
$self->selection_changed(1);
|
$self->selection_changed(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub select_object_from_cpp {
|
||||||
|
my ($self, $obj_idx, $vol_idx) = @_;
|
||||||
|
|
||||||
|
# remove current selection
|
||||||
|
foreach my $o (0..$#{$self->{objects}}) {
|
||||||
|
$self->{objects}->[$o]->selected(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $curr = Slic3r::GUI::_3DScene::get_select_by($self->{canvas3D});
|
||||||
|
|
||||||
|
if (defined $obj_idx) {
|
||||||
|
if ($vol_idx == -1){
|
||||||
|
if ($curr eq 'object') {
|
||||||
|
$self->{objects}->[$obj_idx]->selected(1);
|
||||||
|
}
|
||||||
|
elsif ($curr eq 'volume') {
|
||||||
|
Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'object');
|
||||||
|
}
|
||||||
|
|
||||||
|
my $selections = $self->collect_selections;
|
||||||
|
Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
|
||||||
|
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($curr eq 'object') {
|
||||||
|
Slic3r::GUI::_3DScene::set_select_by($self->{canvas3D}, 'volume');
|
||||||
|
}
|
||||||
|
|
||||||
|
my $selections = [];
|
||||||
|
Slic3r::GUI::_3DScene::set_objects_selections($self->{canvas3D}, \@$selections);
|
||||||
|
Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas3D});
|
||||||
|
Slic3r::GUI::_3DScene::reload_scene($self->{canvas3D}, 1);
|
||||||
|
my $volume_idx = Slic3r::GUI::_3DScene::get_first_volume_id($self->{canvas3D}, $obj_idx);
|
||||||
|
|
||||||
|
my $inst_cnt = $self->{model}->objects->[$obj_idx]->instances_count;
|
||||||
|
for (0..$inst_cnt-1){
|
||||||
|
Slic3r::GUI::_3DScene::select_volume($self->{canvas3D}, $vol_idx*$inst_cnt + $_ + $volume_idx) if ($volume_idx != -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->selection_changed(1);
|
||||||
|
}
|
||||||
|
|
||||||
sub selected_object {
|
sub selected_object {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
my $obj_idx = first { $self->{objects}[$_]->selected } 0..$#{ $self->{objects} };
|
my $obj_idx = first { $self->{objects}[$_]->selected } 0..$#{ $self->{objects} };
|
||||||
|
@ -5,12 +5,12 @@ use utf8;
|
|||||||
|
|
||||||
use Slic3r::Print::State ':steps';
|
use Slic3r::Print::State ':steps';
|
||||||
use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE wxCB_READONLY);
|
use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE wxCB_READONLY);
|
||||||
use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX EVT_CHOICE EVT_CHECKLISTBOX);
|
use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX EVT_CHOICE EVT_CHECKLISTBOX EVT_SIZE);
|
||||||
use base qw(Wx::Panel Class::Accessor);
|
use base qw(Wx::Panel Class::Accessor);
|
||||||
|
|
||||||
use Wx::Locale gettext => 'L';
|
use Wx::Locale gettext => 'L';
|
||||||
|
|
||||||
__PACKAGE__->mk_accessors(qw(print gcode_preview_data enabled _loaded canvas slider_low slider_high single_layer));
|
__PACKAGE__->mk_accessors(qw(print gcode_preview_data enabled _loaded canvas slider_low slider_high single_layer double_slider_sizer));
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
@ -27,47 +27,47 @@ sub new {
|
|||||||
Slic3r::GUI::_3DScene::enable_shader($canvas, 1);
|
Slic3r::GUI::_3DScene::enable_shader($canvas, 1);
|
||||||
Slic3r::GUI::_3DScene::set_config($canvas, $config);
|
Slic3r::GUI::_3DScene::set_config($canvas, $config);
|
||||||
$self->canvas($canvas);
|
$self->canvas($canvas);
|
||||||
my $slider_low = Wx::Slider->new(
|
# my $slider_low = Wx::Slider->new(
|
||||||
$self, -1,
|
# $self, -1,
|
||||||
0, # default
|
# 0, # default
|
||||||
0, # min
|
# 0, # min
|
||||||
# we set max to a bogus non-zero value because the MSW implementation of wxSlider
|
# we set max to a bogus non-zero value because the MSW implementation of wxSlider
|
||||||
# will skip drawing the slider if max <= min:
|
# will skip drawing the slider if max <= min:
|
||||||
1, # max
|
# 1, # max
|
||||||
wxDefaultPosition,
|
# wxDefaultPosition,
|
||||||
wxDefaultSize,
|
# wxDefaultSize,
|
||||||
wxVERTICAL | wxSL_INVERSE,
|
# wxVERTICAL | wxSL_INVERSE,
|
||||||
);
|
# );
|
||||||
$self->slider_low($slider_low);
|
# $self->slider_low($slider_low);
|
||||||
my $slider_high = Wx::Slider->new(
|
# my $slider_high = Wx::Slider->new(
|
||||||
$self, -1,
|
# $self, -1,
|
||||||
0, # default
|
# 0, # default
|
||||||
0, # min
|
# 0, # min
|
||||||
# we set max to a bogus non-zero value because the MSW implementation of wxSlider
|
# we set max to a bogus non-zero value because the MSW implementation of wxSlider
|
||||||
# will skip drawing the slider if max <= min:
|
# will skip drawing the slider if max <= min:
|
||||||
1, # max
|
# 1, # max
|
||||||
wxDefaultPosition,
|
# wxDefaultPosition,
|
||||||
wxDefaultSize,
|
# wxDefaultSize,
|
||||||
wxVERTICAL | wxSL_INVERSE,
|
# wxVERTICAL | wxSL_INVERSE,
|
||||||
);
|
# );
|
||||||
$self->slider_high($slider_high);
|
# $self->slider_high($slider_high);
|
||||||
|
|
||||||
my $z_label_low = $self->{z_label_low} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
# my $z_label_low = $self->{z_label_low} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||||
[40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||||
$z_label_low->SetFont($Slic3r::GUI::small_font);
|
# $z_label_low->SetFont($Slic3r::GUI::small_font);
|
||||||
my $z_label_high = $self->{z_label_high} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
# my $z_label_high = $self->{z_label_high} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||||
[40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||||
$z_label_high->SetFont($Slic3r::GUI::small_font);
|
# $z_label_high->SetFont($Slic3r::GUI::small_font);
|
||||||
|
|
||||||
my $z_label_low_idx = $self->{z_label_low_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
# my $z_label_low_idx = $self->{z_label_low_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||||
[40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||||
$z_label_low_idx->SetFont($Slic3r::GUI::small_font);
|
# $z_label_low_idx->SetFont($Slic3r::GUI::small_font);
|
||||||
my $z_label_high_idx = $self->{z_label_high_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
# my $z_label_high_idx = $self->{z_label_high_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||||
[40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||||
$z_label_high_idx->SetFont($Slic3r::GUI::small_font);
|
# $z_label_high_idx->SetFont($Slic3r::GUI::small_font);
|
||||||
|
|
||||||
$self->single_layer(0);
|
# $self->single_layer(0);
|
||||||
my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, L("1 Layer"));
|
# my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, L("1 Layer"));
|
||||||
|
|
||||||
my $label_view_type = $self->{label_view_type} = Wx::StaticText->new($self, -1, L("View"));
|
my $label_view_type = $self->{label_view_type} = Wx::StaticText->new($self, -1, L("View"));
|
||||||
|
|
||||||
@ -102,26 +102,31 @@ sub new {
|
|||||||
.L("Wipe tower")."|"
|
.L("Wipe tower")."|"
|
||||||
.L("Custom");
|
.L("Custom");
|
||||||
Slic3r::GUI::create_combochecklist($combochecklist_features, $feature_text, $feature_items, 1);
|
Slic3r::GUI::create_combochecklist($combochecklist_features, $feature_text, $feature_items, 1);
|
||||||
|
|
||||||
|
my $double_slider_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
|
Slic3r::GUI::create_double_slider($self, $double_slider_sizer, $self->canvas);
|
||||||
|
$self->double_slider_sizer($double_slider_sizer);
|
||||||
|
|
||||||
my $checkbox_travel = $self->{checkbox_travel} = Wx::CheckBox->new($self, -1, L("Travel"));
|
my $checkbox_travel = $self->{checkbox_travel} = Wx::CheckBox->new($self, -1, L("Travel"));
|
||||||
my $checkbox_retractions = $self->{checkbox_retractions} = Wx::CheckBox->new($self, -1, L("Retractions"));
|
my $checkbox_retractions = $self->{checkbox_retractions} = Wx::CheckBox->new($self, -1, L("Retractions"));
|
||||||
my $checkbox_unretractions = $self->{checkbox_unretractions} = Wx::CheckBox->new($self, -1, L("Unretractions"));
|
my $checkbox_unretractions = $self->{checkbox_unretractions} = Wx::CheckBox->new($self, -1, L("Unretractions"));
|
||||||
my $checkbox_shells = $self->{checkbox_shells} = Wx::CheckBox->new($self, -1, L("Shells"));
|
my $checkbox_shells = $self->{checkbox_shells} = Wx::CheckBox->new($self, -1, L("Shells"));
|
||||||
|
|
||||||
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
# my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
# my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
my $vsizer_outer = Wx::BoxSizer->new(wxVERTICAL);
|
# my $vsizer_outer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
$vsizer->Add($slider_low, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
# $vsizer->Add($slider_low, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||||
$vsizer->Add($z_label_low_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
# $vsizer->Add($z_label_low_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||||
$vsizer->Add($z_label_low, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
# $vsizer->Add($z_label_low, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||||
$hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
# $hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
||||||
$vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
# $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
$vsizer->Add($slider_high, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
# $vsizer->Add($slider_high, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||||
$vsizer->Add($z_label_high_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
# $vsizer->Add($z_label_high_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||||
$vsizer->Add($z_label_high, 0, 0, 0);
|
# $vsizer->Add($z_label_high, 0, 0, 0);
|
||||||
$hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
# $hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
||||||
$vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
# $vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||||
$vsizer_outer->Add($checkbox_singlelayer, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5);
|
# $vsizer_outer->Add($double_slider_sizer, 3, wxEXPAND, 0);
|
||||||
|
# $vsizer_outer->Add($checkbox_singlelayer, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5);
|
||||||
|
|
||||||
my $bottom_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
my $bottom_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
$bottom_sizer->Add($label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5);
|
$bottom_sizer->Add($label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5);
|
||||||
@ -140,81 +145,83 @@ sub new {
|
|||||||
|
|
||||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
$sizer->Add($canvas, 1, wxALL | wxEXPAND, 0);
|
$sizer->Add($canvas, 1, wxALL | wxEXPAND, 0);
|
||||||
$sizer->Add($vsizer_outer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5);
|
# $sizer->Add($vsizer_outer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5);
|
||||||
|
$sizer->Add($double_slider_sizer, 0, wxEXPAND, 0);#wxTOP | wxBOTTOM | wxEXPAND, 5);
|
||||||
|
|
||||||
my $main_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
my $main_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
$main_sizer->Add($sizer, 1, wxALL | wxEXPAND, 0);
|
$main_sizer->Add($sizer, 1, wxALL | wxEXPAND, 0);
|
||||||
$main_sizer->Add($bottom_sizer, 0, wxALL | wxEXPAND, 0);
|
$main_sizer->Add($bottom_sizer, 0, wxALL | wxEXPAND, 0);
|
||||||
|
|
||||||
EVT_SLIDER($self, $slider_low, sub {
|
# EVT_SLIDER($self, $slider_low, sub {
|
||||||
$slider_high->SetValue($slider_low->GetValue) if $self->single_layer;
|
# $slider_high->SetValue($slider_low->GetValue) if $self->single_layer;
|
||||||
$self->set_z_idx_low ($slider_low ->GetValue)
|
# $self->set_z_idx_low ($slider_low ->GetValue)
|
||||||
});
|
# });
|
||||||
EVT_SLIDER($self, $slider_high, sub {
|
# EVT_SLIDER($self, $slider_high, sub {
|
||||||
$slider_low->SetValue($slider_high->GetValue) if $self->single_layer;
|
# $slider_low->SetValue($slider_high->GetValue) if $self->single_layer;
|
||||||
$self->set_z_idx_high($slider_high->GetValue)
|
# $self->set_z_idx_high($slider_high->GetValue)
|
||||||
});
|
# });
|
||||||
EVT_KEY_DOWN($canvas, sub {
|
# EVT_KEY_DOWN($canvas, sub {
|
||||||
my ($s, $event) = @_;
|
# my ($s, $event) = @_;
|
||||||
my $key = $event->GetKeyCode;
|
# Slic3r::GUI::update_double_slider_from_canvas($event);
|
||||||
if ($event->HasModifiers) {
|
# my $key = $event->GetKeyCode;
|
||||||
$event->Skip;
|
# if ($event->HasModifiers) {
|
||||||
} else {
|
# $event->Skip;
|
||||||
if ($key == ord('U')) {
|
# } else {
|
||||||
$slider_high->SetValue($slider_high->GetValue + 1);
|
# if ($key == ord('U')) {
|
||||||
$slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown());
|
# $slider_high->SetValue($slider_high->GetValue + 1);
|
||||||
$self->set_z_idx_high($slider_high->GetValue);
|
# $slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown());
|
||||||
} elsif ($key == ord('D')) {
|
# $self->set_z_idx_high($slider_high->GetValue);
|
||||||
$slider_high->SetValue($slider_high->GetValue - 1);
|
# } elsif ($key == ord('D')) {
|
||||||
$slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown());
|
# $slider_high->SetValue($slider_high->GetValue - 1);
|
||||||
$self->set_z_idx_high($slider_high->GetValue);
|
# $slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown());
|
||||||
} elsif ($key == ord('S')) {
|
# $self->set_z_idx_high($slider_high->GetValue);
|
||||||
$checkbox_singlelayer->SetValue(! $checkbox_singlelayer->GetValue());
|
# } elsif ($key == ord('S')) {
|
||||||
$self->single_layer($checkbox_singlelayer->GetValue());
|
# $checkbox_singlelayer->SetValue(! $checkbox_singlelayer->GetValue());
|
||||||
if ($self->single_layer) {
|
# $self->single_layer($checkbox_singlelayer->GetValue());
|
||||||
$slider_low->SetValue($slider_high->GetValue);
|
# if ($self->single_layer) {
|
||||||
$self->set_z_idx_high($slider_high->GetValue);
|
# $slider_low->SetValue($slider_high->GetValue);
|
||||||
}
|
# $self->set_z_idx_high($slider_high->GetValue);
|
||||||
} else {
|
# }
|
||||||
$event->Skip;
|
# } else {
|
||||||
}
|
# $event->Skip;
|
||||||
}
|
# }
|
||||||
});
|
# }
|
||||||
EVT_KEY_DOWN($slider_low, sub {
|
# });
|
||||||
my ($s, $event) = @_;
|
# EVT_KEY_DOWN($slider_low, sub {
|
||||||
my $key = $event->GetKeyCode;
|
# my ($s, $event) = @_;
|
||||||
if ($event->HasModifiers) {
|
# my $key = $event->GetKeyCode;
|
||||||
$event->Skip;
|
# if ($event->HasModifiers) {
|
||||||
} else {
|
# $event->Skip;
|
||||||
if ($key == WXK_LEFT) {
|
# } else {
|
||||||
} elsif ($key == WXK_RIGHT) {
|
# if ($key == WXK_LEFT) {
|
||||||
$slider_high->SetFocus;
|
# } elsif ($key == WXK_RIGHT) {
|
||||||
} else {
|
# $slider_high->SetFocus;
|
||||||
$event->Skip;
|
# } else {
|
||||||
}
|
# $event->Skip;
|
||||||
}
|
# }
|
||||||
});
|
# }
|
||||||
EVT_KEY_DOWN($slider_high, sub {
|
# });
|
||||||
my ($s, $event) = @_;
|
# EVT_KEY_DOWN($slider_high, sub {
|
||||||
my $key = $event->GetKeyCode;
|
# my ($s, $event) = @_;
|
||||||
if ($event->HasModifiers) {
|
# my $key = $event->GetKeyCode;
|
||||||
$event->Skip;
|
# if ($event->HasModifiers) {
|
||||||
} else {
|
# $event->Skip;
|
||||||
if ($key == WXK_LEFT) {
|
# } else {
|
||||||
$slider_low->SetFocus;
|
# if ($key == WXK_LEFT) {
|
||||||
} elsif ($key == WXK_RIGHT) {
|
# $slider_low->SetFocus;
|
||||||
} else {
|
# } elsif ($key == WXK_RIGHT) {
|
||||||
$event->Skip;
|
# } else {
|
||||||
}
|
# $event->Skip;
|
||||||
}
|
# }
|
||||||
});
|
# }
|
||||||
EVT_CHECKBOX($self, $checkbox_singlelayer, sub {
|
# });
|
||||||
$self->single_layer($checkbox_singlelayer->GetValue());
|
# EVT_CHECKBOX($self, $checkbox_singlelayer, sub {
|
||||||
if ($self->single_layer) {
|
# $self->single_layer($checkbox_singlelayer->GetValue());
|
||||||
$slider_low->SetValue($slider_high->GetValue);
|
# if ($self->single_layer) {
|
||||||
$self->set_z_idx_high($slider_high->GetValue);
|
# $slider_low->SetValue($slider_high->GetValue);
|
||||||
}
|
# $self->set_z_idx_high($slider_high->GetValue);
|
||||||
});
|
# }
|
||||||
|
# });
|
||||||
EVT_CHOICE($self, $choice_view_type, sub {
|
EVT_CHOICE($self, $choice_view_type, sub {
|
||||||
my $selection = $choice_view_type->GetCurrentSelection();
|
my $selection = $choice_view_type->GetCurrentSelection();
|
||||||
$self->{preferred_color_mode} = ($selection == $self->{tool_idx}) ? 'tool' : 'feature';
|
$self->{preferred_color_mode} = ($selection == $self->{tool_idx}) ? 'tool' : 'feature';
|
||||||
@ -243,6 +250,12 @@ sub new {
|
|||||||
$self->gcode_preview_data->set_shells_visible($checkbox_shells->IsChecked());
|
$self->gcode_preview_data->set_shells_visible($checkbox_shells->IsChecked());
|
||||||
$self->refresh_print;
|
$self->refresh_print;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
EVT_SIZE($self, sub {
|
||||||
|
my ($s, $event) = @_;
|
||||||
|
$event->Skip;
|
||||||
|
$self->Refresh;
|
||||||
|
});
|
||||||
|
|
||||||
$self->SetSizer($main_sizer);
|
$self->SetSizer($main_sizer);
|
||||||
$self->SetMinSize($self->GetSize);
|
$self->SetMinSize($self->GetSize);
|
||||||
@ -392,62 +405,69 @@ sub load_print {
|
|||||||
sub reset_sliders {
|
sub reset_sliders {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
$self->enabled(0);
|
$self->enabled(0);
|
||||||
$self->set_z_range(0,0);
|
# $self->set_z_range(0,0);
|
||||||
$self->slider_low->Hide;
|
# $self->slider_low->Hide;
|
||||||
$self->slider_high->Hide;
|
# $self->slider_high->Hide;
|
||||||
$self->{z_label_low}->SetLabel("");
|
# $self->{z_label_low}->SetLabel("");
|
||||||
$self->{z_label_high}->SetLabel("");
|
# $self->{z_label_high}->SetLabel("");
|
||||||
$self->{z_label_low_idx}->SetLabel("");
|
# $self->{z_label_low_idx}->SetLabel("");
|
||||||
$self->{z_label_high_idx}->SetLabel("");
|
# $self->{z_label_high_idx}->SetLabel("");
|
||||||
|
|
||||||
|
Slic3r::GUI::reset_double_slider();
|
||||||
|
$self->double_slider_sizer->Hide(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub update_sliders
|
sub update_sliders
|
||||||
{
|
{
|
||||||
my ($self, $n_layers) = @_;
|
my ($self, $n_layers) = @_;
|
||||||
|
|
||||||
my $z_idx_low = $self->slider_low->GetValue;
|
# my $z_idx_low = $self->slider_low->GetValue;
|
||||||
my $z_idx_high = $self->slider_high->GetValue;
|
# my $z_idx_high = $self->slider_high->GetValue;
|
||||||
$self->enabled(1);
|
$self->enabled(1);
|
||||||
$self->slider_low->SetRange(0, $n_layers - 1);
|
# $self->slider_low->SetRange(0, $n_layers - 1);
|
||||||
$self->slider_high->SetRange(0, $n_layers - 1);
|
# $self->slider_high->SetRange(0, $n_layers - 1);
|
||||||
|
|
||||||
if ($self->{force_sliders_full_range}) {
|
# if ($self->{force_sliders_full_range}) {
|
||||||
$z_idx_low = 0;
|
# $z_idx_low = 0;
|
||||||
$z_idx_high = $n_layers - 1;
|
# $z_idx_high = $n_layers - 1;
|
||||||
} elsif ($z_idx_high < $n_layers && ($self->single_layer || $z_idx_high != 0)) {
|
# } elsif ($z_idx_high < $n_layers && ($self->single_layer || $z_idx_high != 0)) {
|
||||||
# search new indices for nearest z (size of $self->{layers_z} may change in dependence of what is shown)
|
# # search new indices for nearest z (size of $self->{layers_z} may change in dependence of what is shown)
|
||||||
if (defined($self->{z_low})) {
|
# if (defined($self->{z_low})) {
|
||||||
for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
|
# for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
|
||||||
if ($self->{layers_z}[$i] <= $self->{z_low}) {
|
# if ($self->{layers_z}[$i] <= $self->{z_low}) {
|
||||||
$z_idx_low = $i;
|
# $z_idx_low = $i;
|
||||||
last;
|
# last;
|
||||||
}
|
# }
|
||||||
}
|
# }
|
||||||
}
|
# }
|
||||||
if (defined($self->{z_high})) {
|
# if (defined($self->{z_high})) {
|
||||||
for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
|
# for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
|
||||||
if ($self->{layers_z}[$i] <= $self->{z_high}) {
|
# if ($self->{layers_z}[$i] <= $self->{z_high}) {
|
||||||
$z_idx_high = $i;
|
# $z_idx_high = $i;
|
||||||
last;
|
# last;
|
||||||
}
|
# }
|
||||||
}
|
# }
|
||||||
}
|
# }
|
||||||
} elsif ($z_idx_high >= $n_layers) {
|
# } elsif ($z_idx_high >= $n_layers) {
|
||||||
# Out of range. Disable 'single layer' view.
|
# # Out of range. Disable 'single layer' view.
|
||||||
$self->single_layer(0);
|
# $self->single_layer(0);
|
||||||
$self->{checkbox_singlelayer}->SetValue(0);
|
# $self->{checkbox_singlelayer}->SetValue(0);
|
||||||
$z_idx_low = 0;
|
# $z_idx_low = 0;
|
||||||
$z_idx_high = $n_layers - 1;
|
# $z_idx_high = $n_layers - 1;
|
||||||
} else {
|
# } else {
|
||||||
$z_idx_low = 0;
|
# $z_idx_low = 0;
|
||||||
$z_idx_high = $n_layers - 1;
|
# $z_idx_high = $n_layers - 1;
|
||||||
}
|
# }
|
||||||
|
|
||||||
$self->slider_low->SetValue($z_idx_low);
|
# $self->slider_low->SetValue($z_idx_low);
|
||||||
$self->slider_high->SetValue($z_idx_high);
|
# $self->slider_high->SetValue($z_idx_high);
|
||||||
$self->slider_low->Show;
|
# $self->slider_low->Show;
|
||||||
$self->slider_high->Show;
|
# $self->slider_high->Show;
|
||||||
$self->set_z_range($self->{layers_z}[$z_idx_low], $self->{layers_z}[$z_idx_high]);
|
# $self->set_z_range($self->{layers_z}[$z_idx_low], $self->{layers_z}[$z_idx_high]);
|
||||||
|
|
||||||
|
Slic3r::GUI::update_double_slider($self->{force_sliders_full_range});
|
||||||
|
$self->double_slider_sizer->Show(0);
|
||||||
|
|
||||||
$self->Layout;
|
$self->Layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
resources/icons/support_blocker.png
Normal file
BIN
resources/icons/support_blocker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 656 B |
BIN
resources/icons/support_enforcer.png
Normal file
BIN
resources/icons/support_enforcer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 509 B |
@ -179,6 +179,7 @@ add_library(libslic3r STATIC
|
|||||||
${LIBDIR}/libslic3r/SurfaceCollection.hpp
|
${LIBDIR}/libslic3r/SurfaceCollection.hpp
|
||||||
${LIBDIR}/libslic3r/SVG.cpp
|
${LIBDIR}/libslic3r/SVG.cpp
|
||||||
${LIBDIR}/libslic3r/SVG.hpp
|
${LIBDIR}/libslic3r/SVG.hpp
|
||||||
|
${LIBDIR}/libslic3r/Technologies.hpp
|
||||||
${LIBDIR}/libslic3r/TriangleMesh.cpp
|
${LIBDIR}/libslic3r/TriangleMesh.cpp
|
||||||
${LIBDIR}/libslic3r/TriangleMesh.hpp
|
${LIBDIR}/libslic3r/TriangleMesh.hpp
|
||||||
${LIBDIR}/libslic3r/SLABasePool.hpp
|
${LIBDIR}/libslic3r/SLABasePool.hpp
|
||||||
@ -568,7 +569,7 @@ if (WIN32)
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# SLIC3R_MSVC_PDB
|
# SLIC3R_MSVC_PDB
|
||||||
if (MSVC AND SLIC3R_MSVC_PDB AND ${CMAKE_BUILD_TYPE} STREQUAL "Release")
|
if (MSVC AND SLIC3R_MSVC_PDB AND "${CMAKE_BUILD_TYPE}" STREQUAL "Release")
|
||||||
set_target_properties(XS PROPERTIES
|
set_target_properties(XS PROPERTIES
|
||||||
COMPILE_FLAGS "/Zi"
|
COMPILE_FLAGS "/Zi"
|
||||||
LINK_FLAGS "/DEBUG /OPT:REF /OPT:ICF"
|
LINK_FLAGS "/DEBUG /OPT:REF /OPT:ICF"
|
||||||
|
@ -640,6 +640,7 @@ public:
|
|||||||
|
|
||||||
// The progress function will be called with the number of placed items
|
// The progress function will be called with the number of placed items
|
||||||
using ProgressFunction = std::function<void(unsigned)>;
|
using ProgressFunction = std::function<void(unsigned)>;
|
||||||
|
using StopCondition = std::function<bool(void)>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper interface (trait) class for any selections strategy provider.
|
* A wrapper interface (trait) class for any selections strategy provider.
|
||||||
@ -674,6 +675,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); }
|
void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); }
|
||||||
|
|
||||||
|
void stopCondition(StopCondition cond) { impl_.stopCondition(cond); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief A method to start the calculation on the input sequence.
|
* \brief A method to start the calculation on the input sequence.
|
||||||
*
|
*
|
||||||
@ -864,6 +867,11 @@ public:
|
|||||||
selector_.progressIndicator(func); return *this;
|
selector_.progressIndicator(func); return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set a predicate to tell when to abort nesting.
|
||||||
|
inline Nester& stopCondition(StopCondition fn) {
|
||||||
|
selector_.stopCondition(fn); return *this;
|
||||||
|
}
|
||||||
|
|
||||||
inline PackGroup lastResult() {
|
inline PackGroup lastResult() {
|
||||||
PackGroup ret;
|
PackGroup ret;
|
||||||
for(size_t i = 0; i < selector_.binCount(); i++) {
|
for(size_t i = 0; i < selector_.binCount(); i++) {
|
||||||
|
@ -551,7 +551,7 @@ public:
|
|||||||
// Safety test: try to pack each item into an empty bin. If it fails
|
// Safety test: try to pack each item into an empty bin. If it fails
|
||||||
// then it should be removed from the not_packed list
|
// then it should be removed from the not_packed list
|
||||||
{ auto it = store_.begin();
|
{ auto it = store_.begin();
|
||||||
while (it != store_.end()) {
|
while (it != store_.end() && !this->stopcond_()) {
|
||||||
Placer p(bin); p.configure(pconfig);
|
Placer p(bin); p.configure(pconfig);
|
||||||
if(!p.pack(*it, rem(it, store_))) {
|
if(!p.pack(*it, rem(it, store_))) {
|
||||||
it = store_.erase(it);
|
it = store_.erase(it);
|
||||||
@ -592,9 +592,11 @@ public:
|
|||||||
|
|
||||||
bool do_pairs = config_.try_pairs;
|
bool do_pairs = config_.try_pairs;
|
||||||
bool do_triplets = config_.try_triplets;
|
bool do_triplets = config_.try_triplets;
|
||||||
|
StopCondition stopcond = this->stopcond_;
|
||||||
|
|
||||||
// The DJD heuristic algorithm itself:
|
// The DJD heuristic algorithm itself:
|
||||||
auto packjob = [INITIAL_FILL_AREA, bin_area, w, do_triplets, do_pairs,
|
auto packjob = [INITIAL_FILL_AREA, bin_area, w, do_triplets, do_pairs,
|
||||||
|
stopcond,
|
||||||
&tryOneByOne,
|
&tryOneByOne,
|
||||||
&tryGroupsOfTwo,
|
&tryGroupsOfTwo,
|
||||||
&tryGroupsOfThree,
|
&tryGroupsOfThree,
|
||||||
@ -606,12 +608,12 @@ public:
|
|||||||
double waste = .0;
|
double waste = .0;
|
||||||
bool lasttry = false;
|
bool lasttry = false;
|
||||||
|
|
||||||
while(!not_packed.empty()) {
|
while(!not_packed.empty() && !stopcond()) {
|
||||||
|
|
||||||
{// Fill the bin up to INITIAL_FILL_PROPORTION of its capacity
|
{// Fill the bin up to INITIAL_FILL_PROPORTION of its capacity
|
||||||
auto it = not_packed.begin();
|
auto it = not_packed.begin();
|
||||||
|
|
||||||
while(it != not_packed.end() &&
|
while(it != not_packed.end() && !stopcond() &&
|
||||||
filled_area < INITIAL_FILL_AREA)
|
filled_area < INITIAL_FILL_AREA)
|
||||||
{
|
{
|
||||||
if(placer.pack(*it, rem(it, not_packed))) {
|
if(placer.pack(*it, rem(it, not_packed))) {
|
||||||
@ -623,14 +625,14 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// try pieses one by one
|
// try pieces one by one
|
||||||
while(tryOneByOne(placer, not_packed, waste, free_area,
|
while(tryOneByOne(placer, not_packed, waste, free_area,
|
||||||
filled_area)) {
|
filled_area)) {
|
||||||
waste = 0; lasttry = false;
|
waste = 0; lasttry = false;
|
||||||
makeProgress(placer, idx, 1);
|
makeProgress(placer, idx, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// try groups of 2 pieses
|
// try groups of 2 pieces
|
||||||
while(do_pairs &&
|
while(do_pairs &&
|
||||||
tryGroupsOfTwo(placer, not_packed, waste, free_area,
|
tryGroupsOfTwo(placer, not_packed, waste, free_area,
|
||||||
filled_area)) {
|
filled_area)) {
|
||||||
@ -638,7 +640,7 @@ public:
|
|||||||
makeProgress(placer, idx, 2);
|
makeProgress(placer, idx, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// try groups of 3 pieses
|
// try groups of 3 pieces
|
||||||
while(do_triplets &&
|
while(do_triplets &&
|
||||||
tryGroupsOfThree(placer, not_packed, waste, free_area,
|
tryGroupsOfThree(placer, not_packed, waste, free_area,
|
||||||
filled_area)) {
|
filled_area)) {
|
||||||
|
@ -60,7 +60,7 @@ public:
|
|||||||
placer.configure(pconfig);
|
placer.configure(pconfig);
|
||||||
|
|
||||||
auto it = store_.begin();
|
auto it = store_.begin();
|
||||||
while(it != store_.end()) {
|
while(it != store_.end() && !this->stopcond_()) {
|
||||||
if(!placer.pack(*it, {std::next(it), store_.end()})) {
|
if(!placer.pack(*it, {std::next(it), store_.end()})) {
|
||||||
if(packed_bins_.back().empty()) ++it;
|
if(packed_bins_.back().empty()) ++it;
|
||||||
placer.clearItems();
|
placer.clearItems();
|
||||||
|
@ -56,10 +56,12 @@ public:
|
|||||||
this->progress_(static_cast<unsigned>(--total));
|
this->progress_(static_cast<unsigned>(--total));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto& cancelled = this->stopcond_;
|
||||||
|
|
||||||
// Safety test: try to pack each item into an empty bin. If it fails
|
// Safety test: try to pack each item into an empty bin. If it fails
|
||||||
// then it should be removed from the list
|
// then it should be removed from the list
|
||||||
{ auto it = store_.begin();
|
{ auto it = store_.begin();
|
||||||
while (it != store_.end()) {
|
while (it != store_.end() && !cancelled()) {
|
||||||
Placer p(bin); p.configure(pconfig);
|
Placer p(bin); p.configure(pconfig);
|
||||||
if(!p.pack(*it)) {
|
if(!p.pack(*it)) {
|
||||||
it = store_.erase(it);
|
it = store_.erase(it);
|
||||||
@ -67,13 +69,14 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
auto it = store_.begin();
|
auto it = store_.begin();
|
||||||
|
|
||||||
while(it != store_.end()) {
|
while(it != store_.end() && !cancelled()) {
|
||||||
bool was_packed = false;
|
bool was_packed = false;
|
||||||
size_t j = 0;
|
size_t j = 0;
|
||||||
while(!was_packed) {
|
while(!was_packed && !cancelled()) {
|
||||||
for(; j < placers.size() && !was_packed; j++) {
|
for(; j < placers.size() && !was_packed && !cancelled(); j++) {
|
||||||
if((was_packed = placers[j].pack(*it, rem(it, store_) )))
|
if((was_packed = placers[j].pack(*it, rem(it, store_) )))
|
||||||
makeProgress(placers[j], j);
|
makeProgress(placers[j], j);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define SELECTION_BOILERPLATE_HPP
|
#define SELECTION_BOILERPLATE_HPP
|
||||||
|
|
||||||
#include "../libnest2d.hpp"
|
#include "../libnest2d.hpp"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
namespace libnest2d { namespace selections {
|
namespace libnest2d { namespace selections {
|
||||||
|
|
||||||
@ -25,14 +26,15 @@ public:
|
|||||||
return packed_bins_[binIndex];
|
return packed_bins_[binIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void progressIndicator(ProgressFunction fn) {
|
inline void progressIndicator(ProgressFunction fn) { progress_ = fn; }
|
||||||
progress_ = fn;
|
|
||||||
}
|
inline void stopCondition(StopCondition cond) { stopcond_ = cond; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
PackGroup packed_bins_;
|
PackGroup packed_bins_;
|
||||||
ProgressFunction progress_ = [](unsigned){};
|
ProgressFunction progress_ = [](unsigned){};
|
||||||
|
StopCondition stopcond_ = [](){ return false; };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1225,8 +1225,10 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
|
Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) const
|
||||||
{
|
{
|
||||||
|
assert(std::abs(2 * offset) < m_resolution);
|
||||||
|
|
||||||
typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
|
typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
|
||||||
// 0) Prepare a binary grid.
|
// 0) Prepare a binary grid.
|
||||||
size_t cell_rows = m_rows + 2;
|
size_t cell_rows = m_rows + 2;
|
||||||
@ -1237,7 +1239,7 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
|
|||||||
cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1);
|
cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1);
|
||||||
// Fill in empty cells, which have a left / right neighbor filled.
|
// Fill in empty cells, which have a left / right neighbor filled.
|
||||||
// Fill in empty cells, which have the top / bottom neighbor filled.
|
// Fill in empty cells, which have the top / bottom neighbor filled.
|
||||||
{
|
if (fill_holes) {
|
||||||
std::vector<char> cell_inside2(cell_inside);
|
std::vector<char> cell_inside2(cell_inside);
|
||||||
for (int r = 1; r + 1 < int(cell_rows); ++ r) {
|
for (int r = 1; r + 1 < int(cell_rows); ++ r) {
|
||||||
for (int c = 1; c + 1 < int(cell_cols); ++ c) {
|
for (int c = 1; c + 1 < int(cell_cols); ++ c) {
|
||||||
|
@ -58,7 +58,7 @@ public:
|
|||||||
const size_t cols() const { return m_cols; }
|
const size_t cols() const { return m_cols; }
|
||||||
|
|
||||||
// For supports: Contours enclosing the rasterized edges.
|
// For supports: Contours enclosing the rasterized edges.
|
||||||
Polygons contours_simplified(coord_t offset) const;
|
Polygons contours_simplified(coord_t offset, bool fill_holes) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct Cell {
|
struct Cell {
|
||||||
|
@ -61,12 +61,11 @@ ExPolygonCollection::rotate(double angle, const Point ¢er)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
bool
|
bool ExPolygonCollection::contains(const T &item) const
|
||||||
ExPolygonCollection::contains(const T &item) const
|
|
||||||
{
|
{
|
||||||
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
|
for (const ExPolygon &poly : this->expolygons)
|
||||||
if (it->contains(item)) return true;
|
if (poly.contains(item))
|
||||||
}
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
template bool ExPolygonCollection::contains<Point>(const Point &item) const;
|
template bool ExPolygonCollection::contains<Point>(const Point &item) const;
|
||||||
|
@ -91,6 +91,8 @@ public:
|
|||||||
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
||||||
virtual double min_mm3_per_mm() const = 0;
|
virtual double min_mm3_per_mm() const = 0;
|
||||||
virtual Polyline as_polyline() const = 0;
|
virtual Polyline as_polyline() const = 0;
|
||||||
|
virtual void collect_polylines(Polylines &dst) const = 0;
|
||||||
|
virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; }
|
||||||
virtual double length() const = 0;
|
virtual double length() const = 0;
|
||||||
virtual double total_volume() const = 0;
|
virtual double total_volume() const = 0;
|
||||||
};
|
};
|
||||||
@ -123,8 +125,11 @@ public:
|
|||||||
|
|
||||||
ExtrusionPath* clone() const { return new ExtrusionPath (*this); }
|
ExtrusionPath* clone() const { return new ExtrusionPath (*this); }
|
||||||
void reverse() { this->polyline.reverse(); }
|
void reverse() { this->polyline.reverse(); }
|
||||||
Point first_point() const { return this->polyline.points.front(); }
|
Point first_point() const override { return this->polyline.points.front(); }
|
||||||
Point last_point() const { return this->polyline.points.back(); }
|
Point last_point() const override { return this->polyline.points.back(); }
|
||||||
|
size_t size() const { return this->polyline.size(); }
|
||||||
|
bool empty() const { return this->polyline.empty(); }
|
||||||
|
bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
|
||||||
// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
|
// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
|
||||||
// Currently not used.
|
// Currently not used.
|
||||||
void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
|
void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
|
||||||
@ -133,8 +138,8 @@ public:
|
|||||||
void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
|
void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
|
||||||
void clip_end(double distance);
|
void clip_end(double distance);
|
||||||
void simplify(double tolerance);
|
void simplify(double tolerance);
|
||||||
virtual double length() const;
|
double length() const override;
|
||||||
virtual ExtrusionRole role() const { return m_role; }
|
ExtrusionRole role() const override { return m_role; }
|
||||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
||||||
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
||||||
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
|
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
|
||||||
@ -149,7 +154,8 @@ public:
|
|||||||
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
||||||
double min_mm3_per_mm() const { return this->mm3_per_mm; }
|
double min_mm3_per_mm() const { return this->mm3_per_mm; }
|
||||||
Polyline as_polyline() const { return this->polyline; }
|
Polyline as_polyline() const { return this->polyline; }
|
||||||
virtual double total_volume() const { return mm3_per_mm * unscale<double>(length()); }
|
void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); }
|
||||||
|
double total_volume() const override { return mm3_per_mm * unscale<double>(length()); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
|
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
|
||||||
@ -178,10 +184,10 @@ public:
|
|||||||
bool can_reverse() const { return true; }
|
bool can_reverse() const { return true; }
|
||||||
ExtrusionMultiPath* clone() const { return new ExtrusionMultiPath(*this); }
|
ExtrusionMultiPath* clone() const { return new ExtrusionMultiPath(*this); }
|
||||||
void reverse();
|
void reverse();
|
||||||
Point first_point() const { return this->paths.front().polyline.points.front(); }
|
Point first_point() const override { return this->paths.front().polyline.points.front(); }
|
||||||
Point last_point() const { return this->paths.back().polyline.points.back(); }
|
Point last_point() const override { return this->paths.back().polyline.points.back(); }
|
||||||
virtual double length() const;
|
double length() const override;
|
||||||
virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
|
ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
|
||||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
||||||
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
||||||
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
|
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
|
||||||
@ -196,7 +202,8 @@ public:
|
|||||||
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
||||||
double min_mm3_per_mm() const;
|
double min_mm3_per_mm() const;
|
||||||
Polyline as_polyline() const;
|
Polyline as_polyline() const;
|
||||||
virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
|
void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
|
||||||
|
double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
|
// Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
|
||||||
@ -218,18 +225,18 @@ public:
|
|||||||
bool make_clockwise();
|
bool make_clockwise();
|
||||||
bool make_counter_clockwise();
|
bool make_counter_clockwise();
|
||||||
void reverse();
|
void reverse();
|
||||||
Point first_point() const { return this->paths.front().polyline.points.front(); }
|
Point first_point() const override { return this->paths.front().polyline.points.front(); }
|
||||||
Point last_point() const { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
|
Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
|
||||||
Polygon polygon() const;
|
Polygon polygon() const;
|
||||||
virtual double length() const;
|
double length() const override;
|
||||||
bool split_at_vertex(const Point &point);
|
bool split_at_vertex(const Point &point);
|
||||||
void split_at(const Point &point, bool prefer_non_overhang);
|
void split_at(const Point &point, bool prefer_non_overhang);
|
||||||
void clip_end(double distance, ExtrusionPaths* paths) const;
|
void clip_end(double distance, ExtrusionPaths* paths) const;
|
||||||
// Test, whether the point is extruded by a bridging flow.
|
// Test, whether the point is extruded by a bridging flow.
|
||||||
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
|
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
|
||||||
bool has_overhang_point(const Point &point) const;
|
bool has_overhang_point(const Point &point) const;
|
||||||
virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
|
ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
|
||||||
ExtrusionLoopRole loop_role() const { return m_loop_role; }
|
ExtrusionLoopRole loop_role() const { return m_loop_role; }
|
||||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
||||||
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
||||||
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
|
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
|
||||||
@ -244,7 +251,8 @@ public:
|
|||||||
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
||||||
double min_mm3_per_mm() const;
|
double min_mm3_per_mm() const;
|
||||||
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
|
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
|
||||||
virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
|
void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
|
||||||
|
double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExtrusionLoopRole m_loop_role;
|
ExtrusionLoopRole m_loop_role;
|
||||||
|
@ -24,7 +24,7 @@ public:
|
|||||||
explicit operator ExtrusionPaths() const;
|
explicit operator ExtrusionPaths() const;
|
||||||
|
|
||||||
bool is_collection() const { return true; };
|
bool is_collection() const { return true; };
|
||||||
virtual ExtrusionRole role() const {
|
ExtrusionRole role() const override {
|
||||||
ExtrusionRole out = erNone;
|
ExtrusionRole out = erNone;
|
||||||
for (const ExtrusionEntity *ee : entities) {
|
for (const ExtrusionEntity *ee : entities) {
|
||||||
ExtrusionRole er = ee->role();
|
ExtrusionRole er = ee->role();
|
||||||
@ -71,11 +71,11 @@ public:
|
|||||||
Point last_point() const { return this->entities.back()->last_point(); }
|
Point last_point() const { return this->entities.back()->last_point(); }
|
||||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
||||||
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
||||||
virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
|
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
|
||||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
|
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
|
||||||
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
|
||||||
// Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
|
// Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
|
||||||
virtual void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const;
|
void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override;
|
||||||
Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
|
Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
|
||||||
{ Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
|
{ Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
|
||||||
Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
|
Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
|
||||||
@ -84,14 +84,20 @@ public:
|
|||||||
void flatten(ExtrusionEntityCollection* retval) const;
|
void flatten(ExtrusionEntityCollection* retval) const;
|
||||||
ExtrusionEntityCollection flatten() const;
|
ExtrusionEntityCollection flatten() const;
|
||||||
double min_mm3_per_mm() const;
|
double min_mm3_per_mm() const;
|
||||||
virtual double total_volume() const {double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
|
double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
|
||||||
|
|
||||||
// Following methods shall never be called on an ExtrusionEntityCollection.
|
// Following methods shall never be called on an ExtrusionEntityCollection.
|
||||||
Polyline as_polyline() const {
|
Polyline as_polyline() const {
|
||||||
CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
|
CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
|
||||||
return Polyline();
|
return Polyline();
|
||||||
};
|
};
|
||||||
virtual double length() const {
|
|
||||||
|
void collect_polylines(Polylines &dst) const override {
|
||||||
|
for (ExtrusionEntity* extrusion_entity : this->entities)
|
||||||
|
extrusion_entity->collect_polylines(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
double length() const override {
|
||||||
CONFESS("Calling length() on a ExtrusionEntityCollection");
|
CONFESS("Calling length() on a ExtrusionEntityCollection");
|
||||||
return 0.;
|
return 0.;
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,8 @@ void FillHoneycomb::_fill_surface_single(
|
|||||||
Polylines paths;
|
Polylines paths;
|
||||||
{
|
{
|
||||||
Polylines p;
|
Polylines p;
|
||||||
for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
|
for (Polygon &poly : polygons)
|
||||||
p.push_back((Polyline)(*it));
|
p.emplace_back(poly.points);
|
||||||
paths = intersection_pl(p, to_polygons(expolygon));
|
paths = intersection_pl(p, to_polygons(expolygon));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,8 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
|
|||||||
// if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
|
// if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
|
||||||
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
|
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
|
||||||
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value),
|
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value),
|
||||||
false);
|
// bridge_flow_ratio
|
||||||
|
0.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
|
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
|
||||||
@ -127,7 +128,8 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig
|
|||||||
(width.value > 0) ? width : object->config().extrusion_width,
|
(width.value > 0) ? width : object->config().extrusion_width,
|
||||||
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
|
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
|
||||||
(layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)),
|
(layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)),
|
||||||
false);
|
// bridge_flow_ratio
|
||||||
|
0.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
Flow support_material_interface_flow(const PrintObject *object, float layer_height)
|
Flow support_material_interface_flow(const PrintObject *object, float layer_height)
|
||||||
@ -139,7 +141,8 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig
|
|||||||
// if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
|
// if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
|
||||||
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)),
|
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)),
|
||||||
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value),
|
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value),
|
||||||
false);
|
// bridge_flow_ratio
|
||||||
|
0.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@ const char* VOLUME_TYPE = "volume";
|
|||||||
|
|
||||||
const char* NAME_KEY = "name";
|
const char* NAME_KEY = "name";
|
||||||
const char* MODIFIER_KEY = "modifier";
|
const char* MODIFIER_KEY = "modifier";
|
||||||
|
const char* VOLUME_TYPE_KEY = "volume_type";
|
||||||
|
|
||||||
const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
|
const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
|
||||||
const char* VALID_OBJECT_TYPES[] =
|
const char* VALID_OBJECT_TYPES[] =
|
||||||
@ -1252,9 +1253,13 @@ namespace Slic3r {
|
|||||||
// we extract from the given matrix only the values currently used
|
// we extract from the given matrix only the values currently used
|
||||||
|
|
||||||
// translation
|
// translation
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
Vec3d offset(transform(0, 3), transform(1, 3), transform(2, 3));
|
||||||
|
#else
|
||||||
double offset_x = transform(0, 3);
|
double offset_x = transform(0, 3);
|
||||||
double offset_y = transform(1, 3);
|
double offset_y = transform(1, 3);
|
||||||
double offset_z = transform(2, 3);
|
double offset_z = transform(2, 3);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
|
||||||
// scale
|
// scale
|
||||||
double sx = ::sqrt(sqr(transform(0, 0)) + sqr(transform(1, 0)) + sqr(transform(2, 0)));
|
double sx = ::sqrt(sqr(transform(0, 0)) + sqr(transform(1, 0)) + sqr(transform(2, 0)));
|
||||||
@ -1287,8 +1292,12 @@ namespace Slic3r {
|
|||||||
|
|
||||||
double angle_z = (rotation.axis() == Vec3d::UnitZ()) ? rotation.angle() : -rotation.angle();
|
double angle_z = (rotation.axis() == Vec3d::UnitZ()) ? rotation.angle() : -rotation.angle();
|
||||||
|
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
instance.set_offset(offset);
|
||||||
|
#else
|
||||||
instance.offset(0) = offset_x;
|
instance.offset(0) = offset_x;
|
||||||
instance.offset(1) = offset_y;
|
instance.offset(1) = offset_y;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
instance.scaling_factor = sx;
|
instance.scaling_factor = sx;
|
||||||
instance.rotation = angle_z;
|
instance.rotation = angle_z;
|
||||||
}
|
}
|
||||||
@ -1434,7 +1443,9 @@ namespace Slic3r {
|
|||||||
if (metadata.key == NAME_KEY)
|
if (metadata.key == NAME_KEY)
|
||||||
volume->name = metadata.value;
|
volume->name = metadata.value;
|
||||||
else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
|
else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
|
||||||
volume->modifier = true;
|
volume->set_type(ModelVolume::PARAMETER_MODIFIER);
|
||||||
|
else if (metadata.key == VOLUME_TYPE_KEY)
|
||||||
|
volume->set_type(ModelVolume::type_from_string(metadata.value));
|
||||||
else
|
else
|
||||||
volume->config.set_deserialize(metadata.key, metadata.value);
|
volume->config.set_deserialize(metadata.key, metadata.value);
|
||||||
}
|
}
|
||||||
@ -1949,9 +1960,12 @@ namespace Slic3r {
|
|||||||
if (!volume->name.empty())
|
if (!volume->name.empty())
|
||||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
|
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
|
||||||
|
|
||||||
// stores volume's modifier field
|
// stores volume's modifier field (legacy, to support old slicers)
|
||||||
if (volume->modifier)
|
if (volume->is_modifier())
|
||||||
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
|
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
|
||||||
|
// stores volume's type (overrides the modifier field above)
|
||||||
|
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
|
||||||
|
VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
|
||||||
|
|
||||||
// stores volume's config data
|
// stores volume's config data
|
||||||
for (const std::string& key : volume->config.keys())
|
for (const std::string& key : volume->config.keys())
|
||||||
|
@ -29,7 +29,12 @@
|
|||||||
// VERSION NUMBERS
|
// VERSION NUMBERS
|
||||||
// 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them.
|
// 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them.
|
||||||
// 1 : Introduction of amf versioning. No other change in data saved into amf files.
|
// 1 : Introduction of amf versioning. No other change in data saved into amf files.
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
// 2 : Added z component of offset.
|
||||||
|
const unsigned int VERSION_AMF = 2;
|
||||||
|
#else
|
||||||
const unsigned int VERSION_AMF = 1;
|
const unsigned int VERSION_AMF = 1;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version";
|
const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version";
|
||||||
|
|
||||||
const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config";
|
const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config";
|
||||||
@ -119,19 +124,31 @@ struct AMFParserContext
|
|||||||
NODE_TYPE_INSTANCE, // amf/constellation/instance
|
NODE_TYPE_INSTANCE, // amf/constellation/instance
|
||||||
NODE_TYPE_DELTAX, // amf/constellation/instance/deltax
|
NODE_TYPE_DELTAX, // amf/constellation/instance/deltax
|
||||||
NODE_TYPE_DELTAY, // amf/constellation/instance/deltay
|
NODE_TYPE_DELTAY, // amf/constellation/instance/deltay
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
NODE_TYPE_DELTAZ, // amf/constellation/instance/deltaz
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
NODE_TYPE_RZ, // amf/constellation/instance/rz
|
NODE_TYPE_RZ, // amf/constellation/instance/rz
|
||||||
NODE_TYPE_SCALE, // amf/constellation/instance/scale
|
NODE_TYPE_SCALE, // amf/constellation/instance/scale
|
||||||
NODE_TYPE_METADATA, // anywhere under amf/*/metadata
|
NODE_TYPE_METADATA, // anywhere under amf/*/metadata
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Instance {
|
struct Instance {
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rz_set(false), scale_set(false) {}
|
||||||
|
#else
|
||||||
Instance() : deltax_set(false), deltay_set(false), rz_set(false), scale_set(false) {}
|
Instance() : deltax_set(false), deltay_set(false), rz_set(false), scale_set(false) {}
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
// Shift in the X axis.
|
// Shift in the X axis.
|
||||||
float deltax;
|
float deltax;
|
||||||
bool deltax_set;
|
bool deltax_set;
|
||||||
// Shift in the Y axis.
|
// Shift in the Y axis.
|
||||||
float deltay;
|
float deltay;
|
||||||
bool deltay_set;
|
bool deltay_set;
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
// Shift in the Z axis.
|
||||||
|
float deltaz;
|
||||||
|
bool deltaz_set;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
// Rotation around the Z axis.
|
// Rotation around the Z axis.
|
||||||
float rz;
|
float rz;
|
||||||
bool rz_set;
|
bool rz_set;
|
||||||
@ -254,6 +271,10 @@ void AMFParserContext::startElement(const char *name, const char **atts)
|
|||||||
node_type_new = NODE_TYPE_DELTAX;
|
node_type_new = NODE_TYPE_DELTAX;
|
||||||
else if (strcmp(name, "deltay") == 0)
|
else if (strcmp(name, "deltay") == 0)
|
||||||
node_type_new = NODE_TYPE_DELTAY;
|
node_type_new = NODE_TYPE_DELTAY;
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
else if (strcmp(name, "deltaz") == 0)
|
||||||
|
node_type_new = NODE_TYPE_DELTAZ;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
else if (strcmp(name, "rz") == 0)
|
else if (strcmp(name, "rz") == 0)
|
||||||
node_type_new = NODE_TYPE_RZ;
|
node_type_new = NODE_TYPE_RZ;
|
||||||
else if (strcmp(name, "scale") == 0)
|
else if (strcmp(name, "scale") == 0)
|
||||||
@ -314,7 +335,15 @@ void AMFParserContext::characters(const XML_Char *s, int len)
|
|||||||
{
|
{
|
||||||
switch (m_path.size()) {
|
switch (m_path.size()) {
|
||||||
case 4:
|
case 4:
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
if (m_path.back() == NODE_TYPE_DELTAX ||
|
||||||
|
m_path.back() == NODE_TYPE_DELTAY ||
|
||||||
|
m_path.back() == NODE_TYPE_DELTAZ ||
|
||||||
|
m_path.back() == NODE_TYPE_RZ ||
|
||||||
|
m_path.back() == NODE_TYPE_SCALE)
|
||||||
|
#else
|
||||||
if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALE)
|
if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALE)
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
m_value[0].append(s, len);
|
m_value[0].append(s, len);
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
@ -354,6 +383,14 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||||||
m_instance->deltay_set = true;
|
m_instance->deltay_set = true;
|
||||||
m_value[0].clear();
|
m_value[0].clear();
|
||||||
break;
|
break;
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
case NODE_TYPE_DELTAZ:
|
||||||
|
assert(m_instance);
|
||||||
|
m_instance->deltaz = float(atof(m_value[0].c_str()));
|
||||||
|
m_instance->deltaz_set = true;
|
||||||
|
m_value[0].clear();
|
||||||
|
break;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
case NODE_TYPE_RZ:
|
case NODE_TYPE_RZ:
|
||||||
assert(m_instance);
|
assert(m_instance);
|
||||||
m_instance->rz = float(atof(m_value[0].c_str()));
|
m_instance->rz = float(atof(m_value[0].c_str()));
|
||||||
@ -458,9 +495,14 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||||||
p = end + 1;
|
p = end + 1;
|
||||||
}
|
}
|
||||||
m_object->layer_height_profile_valid = true;
|
m_object->layer_height_profile_valid = true;
|
||||||
} else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume && strcmp(opt_key, "modifier") == 0) {
|
} else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
|
||||||
// Is this volume a modifier volume?
|
if (strcmp(opt_key, "modifier") == 0) {
|
||||||
m_volume->modifier = atoi(m_value[1].c_str()) == 1;
|
// Is this volume a modifier volume?
|
||||||
|
// "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag.
|
||||||
|
m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
|
||||||
|
} else if (strcmp(opt_key, "volume_type") == 0) {
|
||||||
|
m_volume->set_type(ModelVolume::type_from_string(m_value[1]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (m_path.size() == 3) {
|
} else if (m_path.size() == 3) {
|
||||||
if (m_path[1] == NODE_TYPE_MATERIAL) {
|
if (m_path[1] == NODE_TYPE_MATERIAL) {
|
||||||
@ -498,8 +540,12 @@ void AMFParserContext::endDocument()
|
|||||||
for (const Instance &instance : object.second.instances)
|
for (const Instance &instance : object.second.instances)
|
||||||
if (instance.deltax_set && instance.deltay_set) {
|
if (instance.deltax_set && instance.deltay_set) {
|
||||||
ModelInstance *mi = m_model.objects[object.second.idx]->add_instance();
|
ModelInstance *mi = m_model.objects[object.second.idx]->add_instance();
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
mi->set_offset(Vec3d((double)instance.deltax, (double)instance.deltay, (double)instance.deltaz));
|
||||||
|
#else
|
||||||
mi->offset(0) = instance.deltax;
|
mi->offset(0) = instance.deltax;
|
||||||
mi->offset(1) = instance.deltay;
|
mi->offset(1) = instance.deltay;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
mi->rotation = instance.rz_set ? instance.rz : 0.f;
|
mi->rotation = instance.rz_set ? instance.rz : 0.f;
|
||||||
mi->scaling_factor = instance.scale_set ? instance.scale : 1.f;
|
mi->scaling_factor = instance.scale_set ? instance.scale : 1.f;
|
||||||
}
|
}
|
||||||
@ -781,8 +827,9 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
|
|||||||
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
|
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
|
||||||
if (!volume->name.empty())
|
if (!volume->name.empty())
|
||||||
stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
|
stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
|
||||||
if (volume->modifier)
|
if (volume->is_modifier())
|
||||||
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
|
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
|
||||||
|
stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
|
||||||
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
|
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
|
||||||
stream << " <triangle>\n";
|
stream << " <triangle>\n";
|
||||||
for (int j = 0; j < 3; ++j)
|
for (int j = 0; j < 3; ++j)
|
||||||
@ -800,12 +847,21 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
|
|||||||
" <instance objectid=\"" PRINTF_ZU "\">\n"
|
" <instance objectid=\"" PRINTF_ZU "\">\n"
|
||||||
" <deltax>%lf</deltax>\n"
|
" <deltax>%lf</deltax>\n"
|
||||||
" <deltay>%lf</deltay>\n"
|
" <deltay>%lf</deltay>\n"
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
" <deltaz>%lf</deltaz>\n"
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
" <rz>%lf</rz>\n"
|
" <rz>%lf</rz>\n"
|
||||||
" <scale>%lf</scale>\n"
|
" <scale>%lf</scale>\n"
|
||||||
" </instance>\n",
|
" </instance>\n",
|
||||||
object_id,
|
object_id,
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
instance->get_offset(X),
|
||||||
|
instance->get_offset(Y),
|
||||||
|
instance->get_offset(Z),
|
||||||
|
#else
|
||||||
instance->offset(0),
|
instance->offset(0),
|
||||||
instance->offset(1),
|
instance->offset(1),
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
instance->rotation,
|
instance->rotation,
|
||||||
instance->scaling_factor);
|
instance->scaling_factor);
|
||||||
//FIXME missing instance->scaling_factor
|
//FIXME missing instance->scaling_factor
|
||||||
|
@ -166,7 +166,11 @@ bool load_prus(const char *path, Model *model)
|
|||||||
float trafo[3][4] = { 0 };
|
float trafo[3][4] = { 0 };
|
||||||
double instance_rotation = 0.;
|
double instance_rotation = 0.;
|
||||||
double instance_scaling_factor = 1.f;
|
double instance_scaling_factor = 1.f;
|
||||||
Vec2d instance_offset(0., 0.);
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
Vec3d instance_offset = Vec3d::Zero();
|
||||||
|
#else
|
||||||
|
Vec2d instance_offset(0., 0.);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
bool trafo_set = false;
|
bool trafo_set = false;
|
||||||
unsigned int group_id = (unsigned int)-1;
|
unsigned int group_id = (unsigned int)-1;
|
||||||
unsigned int extruder_id = (unsigned int)-1;
|
unsigned int extruder_id = (unsigned int)-1;
|
||||||
@ -207,8 +211,12 @@ bool load_prus(const char *path, Model *model)
|
|||||||
for (size_t c = 0; c < 3; ++ c)
|
for (size_t c = 0; c < 3; ++ c)
|
||||||
trafo[r][c] += mat_trafo(r, c);
|
trafo[r][c] += mat_trafo(r, c);
|
||||||
}
|
}
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
|
||||||
|
#else
|
||||||
instance_offset(0) = position[0] - zero[0];
|
instance_offset(0) = position[0] - zero[0];
|
||||||
instance_offset(1) = position[1] - zero[1];
|
instance_offset(1) = position[1] - zero[1];
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
trafo[2][3] = position[2] / instance_scaling_factor;
|
trafo[2][3] = position[2] / instance_scaling_factor;
|
||||||
trafo_set = true;
|
trafo_set = true;
|
||||||
}
|
}
|
||||||
@ -360,8 +368,12 @@ bool load_prus(const char *path, Model *model)
|
|||||||
ModelInstance *instance = model_object->add_instance();
|
ModelInstance *instance = model_object->add_instance();
|
||||||
instance->rotation = instance_rotation;
|
instance->rotation = instance_rotation;
|
||||||
instance->scaling_factor = instance_scaling_factor;
|
instance->scaling_factor = instance_scaling_factor;
|
||||||
instance->offset = instance_offset;
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
++ num_models;
|
instance->set_offset(instance_offset);
|
||||||
|
#else
|
||||||
|
instance->offset = instance_offset;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
++num_models;
|
||||||
if (group_id != (size_t)-1)
|
if (group_id != (size_t)-1)
|
||||||
group_to_model_object[group_id] = model_object;
|
group_to_model_object[group_id] = model_object;
|
||||||
} else {
|
} else {
|
||||||
|
@ -277,7 +277,6 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::string WipeTowerIntegration::prime(GCode &gcodegen)
|
std::string WipeTowerIntegration::prime(GCode &gcodegen)
|
||||||
{
|
{
|
||||||
assert(m_layer_idx == 0);
|
assert(m_layer_idx == 0);
|
||||||
@ -1000,8 +999,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||||||
print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
|
print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
|
||||||
print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
|
print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
|
||||||
for (const Extruder &extruder : m_writer.extruders()) {
|
for (const Extruder &extruder : m_writer.extruders()) {
|
||||||
double used_filament = extruder.used_filament();
|
double used_filament = extruder.used_filament() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] : 0.f);
|
||||||
double extruded_volume = extruder.extruded_volume();
|
double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
|
||||||
double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
|
double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
|
||||||
double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
|
double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
|
||||||
print.m_print_statistics.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
|
print.m_print_statistics.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
|
||||||
@ -1014,8 +1013,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||||||
_write_format(file, "; filament cost = %.1lf\n", filament_cost);
|
_write_format(file, "; filament cost = %.1lf\n", filament_cost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print.m_print_statistics.total_used_filament = print.m_print_statistics.total_used_filament + used_filament;
|
print.m_print_statistics.total_used_filament += used_filament;
|
||||||
print.m_print_statistics.total_extruded_volume = print.m_print_statistics.total_extruded_volume + extruded_volume;
|
print.m_print_statistics.total_extruded_volume += extruded_volume;
|
||||||
|
print.m_print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
|
||||||
|
print.m_print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
|
||||||
}
|
}
|
||||||
_write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost);
|
_write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost);
|
||||||
_write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
|
_write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
|
||||||
|
@ -98,6 +98,7 @@ public:
|
|||||||
void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; }
|
void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; }
|
||||||
std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
|
std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
|
||||||
std::string finalize(GCode &gcodegen);
|
std::string finalize(GCode &gcodegen);
|
||||||
|
std::vector<float> used_filament_length() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
|
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
|
||||||
|
@ -154,6 +154,12 @@ public:
|
|||||||
// the wipe tower has been completely covered by the tool change extrusions,
|
// 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.
|
// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
|
||||||
virtual bool layer_finished() const = 0;
|
virtual bool layer_finished() const = 0;
|
||||||
|
|
||||||
|
// Returns used filament length per extruder:
|
||||||
|
virtual std::vector<float> get_used_filament() const = 0;
|
||||||
|
|
||||||
|
// Returns total number of toolchanges:
|
||||||
|
virtual int get_number_of_toolchanges() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}; // namespace Slic3r
|
}; // namespace Slic3r
|
||||||
|
@ -111,9 +111,10 @@ public:
|
|||||||
const WipeTower::xy start_pos_rotated() const { return m_start_pos; }
|
const WipeTower::xy start_pos_rotated() const { return m_start_pos; }
|
||||||
const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
|
const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
|
||||||
float elapsed_time() const { return m_elapsed_time; }
|
float elapsed_time() const { return m_elapsed_time; }
|
||||||
|
float get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; }
|
||||||
|
|
||||||
// Extrude with an explicitely provided amount of extrusion.
|
// Extrude with an explicitely provided amount of extrusion.
|
||||||
Writer& extrude_explicit(float x, float y, float e, float f = 0.f)
|
Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false)
|
||||||
{
|
{
|
||||||
if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate))
|
if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate))
|
||||||
// Neither extrusion nor a travel move.
|
// Neither extrusion nor a travel move.
|
||||||
@ -122,6 +123,8 @@ public:
|
|||||||
float dx = x - m_current_pos.x;
|
float dx = x - m_current_pos.x;
|
||||||
float dy = y - m_current_pos.y;
|
float dy = y - m_current_pos.y;
|
||||||
double len = sqrt(dx*dx+dy*dy);
|
double len = sqrt(dx*dx+dy*dy);
|
||||||
|
if (record_length)
|
||||||
|
m_used_filament_length += e;
|
||||||
|
|
||||||
|
|
||||||
// Now do the "internal rotation" with respect to the wipe tower center
|
// Now do the "internal rotation" with respect to the wipe tower center
|
||||||
@ -162,8 +165,8 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f)
|
Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false)
|
||||||
{ return extrude_explicit(dest.x, dest.y, e, f); }
|
{ return extrude_explicit(dest.x, dest.y, e, f, record_length); }
|
||||||
|
|
||||||
// Travel to a new XY position. f=0 means use the current value.
|
// Travel to a new XY position. f=0 means use the current value.
|
||||||
Writer& travel(float x, float y, float f = 0.f)
|
Writer& travel(float x, float y, float f = 0.f)
|
||||||
@ -177,7 +180,7 @@ public:
|
|||||||
{
|
{
|
||||||
float dx = x - m_current_pos.x;
|
float dx = x - m_current_pos.x;
|
||||||
float dy = y - m_current_pos.y;
|
float dy = y - m_current_pos.y;
|
||||||
return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f);
|
return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Writer& extrude(const WipeTower::xy &dest, const float f = 0.f)
|
Writer& extrude(const WipeTower::xy &dest, const float f = 0.f)
|
||||||
@ -259,8 +262,8 @@ public:
|
|||||||
// extrude quickly amount e to x2 with feed f.
|
// extrude quickly amount e to x2 with feed f.
|
||||||
Writer& ram(float x1, float x2, float dy, float e0, float e, float f)
|
Writer& ram(float x1, float x2, float dy, float e0, float e, float f)
|
||||||
{
|
{
|
||||||
extrude_explicit(x1, m_current_pos.y + dy, e0, f);
|
extrude_explicit(x1, m_current_pos.y + dy, e0, f, true);
|
||||||
extrude_explicit(x2, m_current_pos.y, e);
|
extrude_explicit(x2, m_current_pos.y, e, 0.f, true);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,6 +407,7 @@ private:
|
|||||||
float m_last_fan_speed = 0.f;
|
float m_last_fan_speed = 0.f;
|
||||||
int current_temp = -1;
|
int current_temp = -1;
|
||||||
const float m_default_analyzer_line_width;
|
const float m_default_analyzer_line_width;
|
||||||
|
float m_used_filament_length = 0.f;
|
||||||
|
|
||||||
std::string set_format_X(float x)
|
std::string set_format_X(float x)
|
||||||
{
|
{
|
||||||
@ -525,6 +529,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
|||||||
++ m_num_tool_changes;
|
++ m_num_tool_changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear
|
||||||
|
// in the output gcode - we should not remember emitting them (we will output them twice in the worst case)
|
||||||
|
|
||||||
// Reset the extruder current to a normal value.
|
// Reset the extruder current to a normal value.
|
||||||
writer.set_extruder_trimpot(550)
|
writer.set_extruder_trimpot(550)
|
||||||
.feedrate(6000)
|
.feedrate(6000)
|
||||||
@ -537,6 +544,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
|||||||
// so that tool_change() will know to extrude the wipe tower brim:
|
// so that tool_change() will know to extrude the wipe tower brim:
|
||||||
m_print_brim = true;
|
m_print_brim = true;
|
||||||
|
|
||||||
|
// Ask our writer about how much material was consumed:
|
||||||
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
result.priming = true;
|
result.priming = true;
|
||||||
result.print_z = this->m_z_pos;
|
result.print_z = this->m_z_pos;
|
||||||
@ -606,10 +616,10 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
|||||||
toolchange_Load(writer, cleaning_box);
|
toolchange_Load(writer, cleaning_box);
|
||||||
writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
|
writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
|
||||||
toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
|
toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
|
||||||
|
++ m_num_tool_changes;
|
||||||
} else
|
} else
|
||||||
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
|
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
|
||||||
|
|
||||||
++ m_num_tool_changes;
|
|
||||||
m_depth_traversed += wipe_area;
|
m_depth_traversed += wipe_area;
|
||||||
|
|
||||||
if (last_change_in_layer) {// draw perimeter line
|
if (last_change_in_layer) {// draw perimeter line
|
||||||
@ -632,6 +642,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
|||||||
";------------------\n"
|
";------------------\n"
|
||||||
"\n\n");
|
"\n\n");
|
||||||
|
|
||||||
|
// Ask our writer about how much material was consumed:
|
||||||
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
result.priming = false;
|
result.priming = false;
|
||||||
result.print_z = this->m_z_pos;
|
result.print_z = this->m_z_pos;
|
||||||
@ -683,6 +696,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
|
|||||||
|
|
||||||
m_print_brim = false; // Mark the brim as extruded
|
m_print_brim = false; // Mark the brim as extruded
|
||||||
|
|
||||||
|
// Ask our writer about how much material was consumed:
|
||||||
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
result.priming = false;
|
result.priming = false;
|
||||||
result.print_z = this->m_z_pos;
|
result.print_z = this->m_z_pos;
|
||||||
@ -804,8 +820,9 @@ void WipeTowerPrusaMM::toolchange_Unload(
|
|||||||
.load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
|
.load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
|
||||||
.travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
|
.travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
|
||||||
.resume_preview();
|
.resume_preview();
|
||||||
|
if (new_temperature != 0 && (new_temperature != m_old_temperature || m_is_first_layer) ) { // Set the extruder temperature, but don't wait.
|
||||||
if (new_temperature != 0 && new_temperature != m_old_temperature ) { // Set the extruder temperature, but don't wait.
|
// If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset)
|
||||||
|
// However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off).
|
||||||
writer.set_extruder_temp(new_temperature, false);
|
writer.set_extruder_temp(new_temperature, false);
|
||||||
m_old_temperature = new_temperature;
|
m_old_temperature = new_temperature;
|
||||||
}
|
}
|
||||||
@ -849,6 +866,9 @@ void WipeTowerPrusaMM::toolchange_Change(
|
|||||||
const unsigned int new_tool,
|
const unsigned int new_tool,
|
||||||
material_type new_material)
|
material_type new_material)
|
||||||
{
|
{
|
||||||
|
// Ask the writer about how much of the old filament we consumed:
|
||||||
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
// Speed override for the material. Go slow for flex and soluble materials.
|
// Speed override for the material. Go slow for flex and soluble materials.
|
||||||
int speed_override;
|
int speed_override;
|
||||||
switch (new_material) {
|
switch (new_material) {
|
||||||
@ -911,7 +931,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
|
|||||||
const float& xl = cleaning_box.ld.x;
|
const float& xl = cleaning_box.ld.x;
|
||||||
const float& xr = cleaning_box.rd.x;
|
const float& xr = cleaning_box.rd.x;
|
||||||
|
|
||||||
|
|
||||||
// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
|
// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
|
||||||
// the ordered volume, even if it means violating the box. This can later be removed and simply
|
// the ordered volume, even if it means violating the box. This can later be removed and simply
|
||||||
// wipe until the end of the assigned area.
|
// wipe until the end of the assigned area.
|
||||||
@ -926,7 +945,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
|
|||||||
m_left_to_right = !m_left_to_right;
|
m_left_to_right = !m_left_to_right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// now the wiping itself:
|
// now the wiping itself:
|
||||||
for (int i = 0; true; ++i) {
|
for (int i = 0; true; ++i) {
|
||||||
if (i!=0) {
|
if (i!=0) {
|
||||||
@ -935,7 +953,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
|
|||||||
else if (wipe_speed < 2210.f) wipe_speed = 4200.f;
|
else if (wipe_speed < 2210.f) wipe_speed = 4200.f;
|
||||||
else wipe_speed = std::min(4800.f, wipe_speed + 50.f);
|
else wipe_speed = std::min(4800.f, wipe_speed + 50.f);
|
||||||
}
|
}
|
||||||
|
|
||||||
float traversed_x = writer.x();
|
float traversed_x = writer.x();
|
||||||
if (m_left_to_right)
|
if (m_left_to_right)
|
||||||
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
|
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
|
||||||
@ -1050,6 +1068,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
|
|||||||
|
|
||||||
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
|
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
|
||||||
|
|
||||||
|
// Ask our writer about how much material was consumed:
|
||||||
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
result.priming = false;
|
result.priming = false;
|
||||||
result.print_z = this->m_z_pos;
|
result.print_z = this->m_z_pos;
|
||||||
@ -1166,6 +1187,8 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
|
|||||||
|
|
||||||
m_layer_info = m_plan.begin();
|
m_layer_info = m_plan.begin();
|
||||||
m_current_tool = (unsigned int)(-2); // we don't know which extruder to start with - we'll set it according to the first toolchange
|
m_current_tool = (unsigned int)(-2); // we don't know which extruder to start with - we'll set it according to the first toolchange
|
||||||
|
for (auto& used : m_used_filament_length) // reset used filament stats
|
||||||
|
used = 0.f;
|
||||||
|
|
||||||
std::vector<WipeTower::ToolChangeResult> layer_result;
|
std::vector<WipeTower::ToolChangeResult> layer_result;
|
||||||
for (auto layer : m_plan)
|
for (auto layer : m_plan)
|
||||||
|
@ -46,7 +46,7 @@ public:
|
|||||||
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
|
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
|
||||||
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
|
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
|
||||||
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
|
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
|
||||||
m_wipe_tower_pos(x, y),
|
m_wipe_tower_pos(x, y),
|
||||||
m_wipe_tower_width(width),
|
m_wipe_tower_width(width),
|
||||||
m_wipe_tower_rotation_angle(rotation_angle),
|
m_wipe_tower_rotation_angle(rotation_angle),
|
||||||
m_y_shift(0.f),
|
m_y_shift(0.f),
|
||||||
@ -94,6 +94,8 @@ public:
|
|||||||
m_filpar[idx].ramming_step_multiplicator /= 100;
|
m_filpar[idx].ramming_step_multiplicator /= 100;
|
||||||
while (stream >> speed)
|
while (stream >> speed)
|
||||||
m_filpar[idx].ramming_speed.push_back(speed);
|
m_filpar[idx].ramming_speed.push_back(speed);
|
||||||
|
|
||||||
|
m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -172,6 +174,9 @@ public:
|
|||||||
return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
|
return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual std::vector<float> get_used_filament() const override { return m_used_filament_length; }
|
||||||
|
virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; }
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WipeTowerPrusaMM();
|
WipeTowerPrusaMM();
|
||||||
@ -331,6 +336,9 @@ private:
|
|||||||
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
|
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();
|
std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
|
||||||
|
|
||||||
|
// Stores information about used filament length per extruder:
|
||||||
|
std::vector<float> m_used_filament_length;
|
||||||
|
|
||||||
|
|
||||||
// Returns gcode for wipe tower brim
|
// 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
|
||||||
|
@ -15,8 +15,7 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
Flow
|
Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const
|
||||||
LayerRegion::flow(FlowRole role, bool bridge, double width) const
|
|
||||||
{
|
{
|
||||||
return m_region->flow(
|
return m_region->flow(
|
||||||
role,
|
role,
|
||||||
|
@ -242,10 +242,19 @@ void Model::center_instances_around_point(const Vec2d &point)
|
|||||||
for (size_t i = 0; i < o->instances.size(); ++ i)
|
for (size_t i = 0; i < o->instances.size(); ++ i)
|
||||||
bb.merge(o->instance_bounding_box(i, false));
|
bb.merge(o->instance_bounding_box(i, false));
|
||||||
|
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
Vec2d shift2 = point - to_2d(bb.center());
|
||||||
|
Vec3d shift3 = Vec3d(shift2(0), shift2(1), 0.0);
|
||||||
|
#else
|
||||||
Vec2d shift = point - 0.5 * to_2d(bb.size()) - to_2d(bb.min);
|
Vec2d shift = point - 0.5 * to_2d(bb.size()) - to_2d(bb.min);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
for (ModelObject *o : this->objects) {
|
for (ModelObject *o : this->objects) {
|
||||||
for (ModelInstance *i : o->instances)
|
for (ModelInstance *i : o->instances)
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
i->set_offset(i->get_offset() + shift3);
|
||||||
|
#else
|
||||||
i->offset += shift;
|
i->offset += shift;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
o->invalidate_bounding_box();
|
o->invalidate_bounding_box();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,8 +320,13 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
|
|||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
for (ModelObject *o : this->objects) {
|
for (ModelObject *o : this->objects) {
|
||||||
for (ModelInstance *i : o->instances) {
|
for (ModelInstance *i : o->instances) {
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
Vec2d offset_xy = positions[idx] - instance_centers[idx];
|
||||||
|
i->set_offset(Vec3d(offset_xy(0), offset_xy(1), i->get_offset(Z)));
|
||||||
|
#else
|
||||||
i->offset = positions[idx] - instance_centers[idx];
|
i->offset = positions[idx] - instance_centers[idx];
|
||||||
++ idx;
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
++idx;
|
||||||
}
|
}
|
||||||
o->invalidate_bounding_box();
|
o->invalidate_bounding_box();
|
||||||
}
|
}
|
||||||
@ -336,7 +350,11 @@ void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
|
|||||||
for (const ModelInstance *i : instances) {
|
for (const ModelInstance *i : instances) {
|
||||||
for (const Vec2d &pos : positions) {
|
for (const Vec2d &pos : positions) {
|
||||||
ModelInstance *instance = o->add_instance(*i);
|
ModelInstance *instance = o->add_instance(*i);
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
instance->set_offset(instance->get_offset() + Vec3d(pos(0), pos(1), 0.0));
|
||||||
|
#else
|
||||||
instance->offset += pos;
|
instance->offset += pos;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
o->invalidate_bounding_box();
|
o->invalidate_bounding_box();
|
||||||
@ -366,13 +384,21 @@ void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
|
|||||||
ModelObject* object = this->objects.front();
|
ModelObject* object = this->objects.front();
|
||||||
object->clear_instances();
|
object->clear_instances();
|
||||||
|
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
Vec3d ext_size = object->bounding_box().size() + dist * Vec3d::Ones();
|
||||||
|
#else
|
||||||
Vec3d size = object->bounding_box().size();
|
Vec3d size = object->bounding_box().size();
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
|
||||||
for (size_t x_copy = 1; x_copy <= x; ++x_copy) {
|
for (size_t x_copy = 1; x_copy <= x; ++x_copy) {
|
||||||
for (size_t y_copy = 1; y_copy <= y; ++y_copy) {
|
for (size_t y_copy = 1; y_copy <= y; ++y_copy) {
|
||||||
ModelInstance* instance = object->add_instance();
|
ModelInstance* instance = object->add_instance();
|
||||||
instance->offset(0) = (size(0) + dist) * (x_copy-1);
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
instance->offset(1) = (size(1) + dist) * (y_copy-1);
|
instance->set_offset(Vec3d(ext_size(0) * (double)(x_copy - 1), ext_size(1) * (double)(y_copy - 1), 0.0));
|
||||||
|
#else
|
||||||
|
instance->offset(0) = (size(0) + dist) * (x_copy - 1);
|
||||||
|
instance->offset(1) = (size(1) + dist) * (y_copy - 1);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -601,7 +627,8 @@ const BoundingBoxf3& ModelObject::bounding_box() const
|
|||||||
if (! m_bounding_box_valid) {
|
if (! m_bounding_box_valid) {
|
||||||
BoundingBoxf3 raw_bbox;
|
BoundingBoxf3 raw_bbox;
|
||||||
for (const ModelVolume *v : this->volumes)
|
for (const ModelVolume *v : this->volumes)
|
||||||
if (! v->modifier)
|
if (v->is_model_part())
|
||||||
|
// mesh.bounding_box() returns a cached value.
|
||||||
raw_bbox.merge(v->mesh.bounding_box());
|
raw_bbox.merge(v->mesh.bounding_box());
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb;
|
||||||
for (const ModelInstance *i : this->instances)
|
for (const ModelInstance *i : this->instances)
|
||||||
@ -632,7 +659,7 @@ TriangleMesh ModelObject::raw_mesh() const
|
|||||||
{
|
{
|
||||||
TriangleMesh mesh;
|
TriangleMesh mesh;
|
||||||
for (const ModelVolume *v : this->volumes)
|
for (const ModelVolume *v : this->volumes)
|
||||||
if (! v->modifier)
|
if (v->is_model_part())
|
||||||
mesh.merge(v->mesh);
|
mesh.merge(v->mesh);
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
@ -643,7 +670,7 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const
|
|||||||
{
|
{
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb;
|
||||||
for (const ModelVolume *v : this->volumes)
|
for (const ModelVolume *v : this->volumes)
|
||||||
if (! v->modifier) {
|
if (v->is_model_part()) {
|
||||||
if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
|
if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
|
||||||
bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true));
|
bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true));
|
||||||
}
|
}
|
||||||
@ -655,7 +682,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
|
|||||||
{
|
{
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb;
|
||||||
for (ModelVolume *v : this->volumes)
|
for (ModelVolume *v : this->volumes)
|
||||||
if (! v->modifier)
|
if (v->is_model_part())
|
||||||
bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate));
|
bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate));
|
||||||
return bb;
|
return bb;
|
||||||
}
|
}
|
||||||
@ -666,7 +693,7 @@ void ModelObject::center_around_origin()
|
|||||||
// center this object around the origin
|
// center this object around the origin
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb;
|
||||||
for (ModelVolume *v : this->volumes)
|
for (ModelVolume *v : this->volumes)
|
||||||
if (! v->modifier)
|
if (v->is_model_part())
|
||||||
bb.merge(v->mesh.bounding_box());
|
bb.merge(v->mesh.bounding_box());
|
||||||
|
|
||||||
// Shift is the vector from the center of the bottom face of the bounding box to the origin
|
// Shift is the vector from the center of the bottom face of the bounding box to the origin
|
||||||
@ -676,12 +703,21 @@ void ModelObject::center_around_origin()
|
|||||||
this->translate(shift);
|
this->translate(shift);
|
||||||
this->origin_translation += shift;
|
this->origin_translation += shift;
|
||||||
|
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
// set z to zero, translation in z has already been done within the mesh
|
||||||
|
shift(2) = 0.0;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
|
||||||
if (!this->instances.empty()) {
|
if (!this->instances.empty()) {
|
||||||
for (ModelInstance *i : this->instances) {
|
for (ModelInstance *i : this->instances) {
|
||||||
// apply rotation and scaling to vector as well before translating instance,
|
// apply rotation and scaling to vector as well before translating instance,
|
||||||
// in order to leave final position unaltered
|
// in order to leave final position unaltered
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
i->set_offset(i->get_offset() + i->transform_vector(-shift, true));
|
||||||
|
#else
|
||||||
Vec3d i_shift = i->world_matrix(true) * shift;
|
Vec3d i_shift = i->world_matrix(true) * shift;
|
||||||
i->offset -= to_2d(i_shift);
|
i->offset -= to_2d(i_shift);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
}
|
}
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
}
|
}
|
||||||
@ -763,7 +799,7 @@ size_t ModelObject::facets_count() const
|
|||||||
{
|
{
|
||||||
size_t num = 0;
|
size_t num = 0;
|
||||||
for (const ModelVolume *v : this->volumes)
|
for (const ModelVolume *v : this->volumes)
|
||||||
if (! v->modifier)
|
if (v->is_model_part())
|
||||||
num += v->mesh.stl.stats.number_of_facets;
|
num += v->mesh.stl.stats.number_of_facets;
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
@ -771,7 +807,7 @@ size_t ModelObject::facets_count() const
|
|||||||
bool ModelObject::needed_repair() const
|
bool ModelObject::needed_repair() const
|
||||||
{
|
{
|
||||||
for (const ModelVolume *v : this->volumes)
|
for (const ModelVolume *v : this->volumes)
|
||||||
if (! v->modifier && v->mesh.needed_repair())
|
if (v->is_model_part() && v->mesh.needed_repair())
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -787,7 +823,7 @@ void ModelObject::cut(coordf_t z, Model* model) const
|
|||||||
lower->input_file = "";
|
lower->input_file = "";
|
||||||
|
|
||||||
for (ModelVolume *volume : this->volumes) {
|
for (ModelVolume *volume : this->volumes) {
|
||||||
if (volume->modifier) {
|
if (! volume->is_model_part()) {
|
||||||
// don't cut modifiers
|
// don't cut modifiers
|
||||||
upper->add_volume(*volume);
|
upper->add_volume(*volume);
|
||||||
lower->add_volume(*volume);
|
lower->add_volume(*volume);
|
||||||
@ -839,7 +875,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
|||||||
ModelVolume* new_volume = new_object->add_volume(*mesh);
|
ModelVolume* new_volume = new_object->add_volume(*mesh);
|
||||||
new_volume->name = volume->name;
|
new_volume->name = volume->name;
|
||||||
new_volume->config = volume->config;
|
new_volume->config = volume->config;
|
||||||
new_volume->modifier = volume->modifier;
|
new_volume->set_type(volume->type());
|
||||||
new_volume->material_id(volume->material_id());
|
new_volume->material_id(volume->material_id());
|
||||||
|
|
||||||
new_objects->push_back(new_object);
|
new_objects->push_back(new_object);
|
||||||
@ -854,7 +890,7 @@ void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_
|
|||||||
{
|
{
|
||||||
for (const ModelVolume* vol : this->volumes)
|
for (const ModelVolume* vol : this->volumes)
|
||||||
{
|
{
|
||||||
if (!vol->modifier)
|
if (vol->is_model_part())
|
||||||
{
|
{
|
||||||
for (ModelInstance* inst : this->instances)
|
for (ModelInstance* inst : this->instances)
|
||||||
{
|
{
|
||||||
@ -951,6 +987,38 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
|
|||||||
return m_convex_hull;
|
return m_convex_hull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
|
||||||
|
{
|
||||||
|
// Legacy support
|
||||||
|
if (s == "1")
|
||||||
|
return PARAMETER_MODIFIER;
|
||||||
|
// New type (supporting the support enforcers & blockers)
|
||||||
|
if (s == "ModelPart")
|
||||||
|
return MODEL_PART;
|
||||||
|
if (s == "ParameterModifier")
|
||||||
|
return PARAMETER_MODIFIER;
|
||||||
|
if (s == "SupportEnforcer")
|
||||||
|
return SUPPORT_ENFORCER;
|
||||||
|
if (s == "SupportBlocker")
|
||||||
|
return SUPPORT_BLOCKER;
|
||||||
|
assert(s == "0");
|
||||||
|
// Default value if invalud type string received.
|
||||||
|
return MODEL_PART;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ModelVolume::type_to_string(const Type t)
|
||||||
|
{
|
||||||
|
switch (t) {
|
||||||
|
case MODEL_PART: return "ModelPart";
|
||||||
|
case PARAMETER_MODIFIER: return "ParameterModifier";
|
||||||
|
case SUPPORT_ENFORCER: return "SupportEnforcer";
|
||||||
|
case SUPPORT_BLOCKER: return "SupportBlocker";
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return "ModelPart";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Split this volume, append the result to the object owning this volume.
|
// Split this volume, append the result to the object owning this volume.
|
||||||
// Return the number of volumes created from this one.
|
// Return the number of volumes created from this one.
|
||||||
// This is useful to assign different materials to different volumes of an object.
|
// This is useful to assign different materials to different volumes of an object.
|
||||||
@ -1005,8 +1073,13 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
|
|||||||
}
|
}
|
||||||
// Translate the bounding box.
|
// Translate the bounding box.
|
||||||
if (! dont_translate) {
|
if (! dont_translate) {
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
bbox.min += this->m_offset;
|
||||||
|
bbox.max += this->m_offset;
|
||||||
|
#else
|
||||||
Eigen::Map<Vec2d>(bbox.min.data()) += this->offset;
|
Eigen::Map<Vec2d>(bbox.min.data()) += this->offset;
|
||||||
Eigen::Map<Vec2d>(bbox.max.data()) += this->offset;
|
Eigen::Map<Vec2d>(bbox.max.data()) += this->offset;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bbox;
|
return bbox;
|
||||||
@ -1033,7 +1106,11 @@ Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, b
|
|||||||
Transform3d m = Transform3d::Identity();
|
Transform3d m = Transform3d::Identity();
|
||||||
|
|
||||||
if (!dont_translate)
|
if (!dont_translate)
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
m.translate(m_offset);
|
||||||
|
#else
|
||||||
m.translate(Vec3d(offset(0), offset(1), 0.0));
|
m.translate(Vec3d(offset(0), offset(1), 0.0));
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
|
||||||
if (!dont_rotate)
|
if (!dont_rotate)
|
||||||
m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ()));
|
m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ()));
|
||||||
|
@ -166,15 +166,27 @@ public:
|
|||||||
// Configuration parameters specific to an object model geometry or a modifier volume,
|
// Configuration parameters specific to an object model geometry or a modifier volume,
|
||||||
// overriding the global Slic3r settings and the ModelObject settings.
|
// overriding the global Slic3r settings and the ModelObject settings.
|
||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
// Is it an object to be printed, or a modifier volume?
|
|
||||||
bool modifier;
|
enum Type {
|
||||||
|
MODEL_TYPE_INVALID = -1,
|
||||||
|
MODEL_PART = 0,
|
||||||
|
PARAMETER_MODIFIER,
|
||||||
|
SUPPORT_ENFORCER,
|
||||||
|
SUPPORT_BLOCKER,
|
||||||
|
};
|
||||||
|
|
||||||
// A parent object owning this modifier volume.
|
// A parent object owning this modifier volume.
|
||||||
ModelObject* get_object() const { return this->object; };
|
ModelObject* get_object() const { return this->object; };
|
||||||
|
Type type() const { return m_type; }
|
||||||
|
void set_type(const Type t) { m_type = t; }
|
||||||
|
bool is_model_part() const { return m_type == MODEL_PART; }
|
||||||
|
bool is_modifier() const { return m_type == PARAMETER_MODIFIER; }
|
||||||
|
bool is_support_enforcer() const { return m_type == SUPPORT_ENFORCER; }
|
||||||
|
bool is_support_blocker() const { return m_type == SUPPORT_BLOCKER; }
|
||||||
t_model_material_id material_id() const { return this->_material_id; }
|
t_model_material_id material_id() const { return this->_material_id; }
|
||||||
void material_id(t_model_material_id material_id);
|
void material_id(t_model_material_id material_id);
|
||||||
ModelMaterial* material() const;
|
ModelMaterial* material() const;
|
||||||
void set_material(t_model_material_id material_id, const ModelMaterial &material);
|
void set_material(t_model_material_id material_id, const ModelMaterial &material);
|
||||||
// Split this volume, append the result to the object owning this volume.
|
// Split this volume, append the result to the object owning this volume.
|
||||||
// Return the number of volumes created from this one.
|
// Return the number of volumes created from this one.
|
||||||
// This is useful to assign different materials to different volumes of an object.
|
// This is useful to assign different materials to different volumes of an object.
|
||||||
@ -185,24 +197,30 @@ public:
|
|||||||
void calculate_convex_hull();
|
void calculate_convex_hull();
|
||||||
const TriangleMesh& get_convex_hull() const;
|
const TriangleMesh& get_convex_hull() const;
|
||||||
|
|
||||||
|
// Helpers for loading / storing into AMF / 3MF files.
|
||||||
|
static Type type_from_string(const std::string &s);
|
||||||
|
static std::string type_to_string(const Type t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Parent object owning this ModelVolume.
|
// Parent object owning this ModelVolume.
|
||||||
ModelObject* object;
|
ModelObject* object;
|
||||||
t_model_material_id _material_id;
|
// Is it an object to be printed, or a modifier volume?
|
||||||
|
Type m_type;
|
||||||
|
t_model_material_id _material_id;
|
||||||
|
|
||||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object)
|
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object)
|
||||||
{
|
{
|
||||||
if (mesh.stl.stats.number_of_facets > 1)
|
if (mesh.stl.stats.number_of_facets > 1)
|
||||||
calculate_convex_hull();
|
calculate_convex_hull();
|
||||||
}
|
}
|
||||||
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {}
|
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {}
|
||||||
ModelVolume(ModelObject *object, const ModelVolume &other) :
|
ModelVolume(ModelObject *object, const ModelVolume &other) :
|
||||||
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object)
|
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object)
|
||||||
{
|
{
|
||||||
this->material_id(other.material_id());
|
this->material_id(other.material_id());
|
||||||
}
|
}
|
||||||
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
|
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
|
||||||
name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object)
|
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object)
|
||||||
{
|
{
|
||||||
this->material_id(other.material_id());
|
this->material_id(other.material_id());
|
||||||
if (mesh.stl.stats.number_of_facets > 1)
|
if (mesh.stl.stats.number_of_facets > 1)
|
||||||
@ -225,15 +243,32 @@ public:
|
|||||||
|
|
||||||
friend class ModelObject;
|
friend class ModelObject;
|
||||||
|
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
private:
|
||||||
|
Vec3d m_offset; // in unscaled coordinates
|
||||||
|
|
||||||
|
public:
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
|
||||||
double rotation; // Rotation around the Z axis, in radians around mesh center point
|
double rotation; // Rotation around the Z axis, in radians around mesh center point
|
||||||
double scaling_factor;
|
double scaling_factor;
|
||||||
|
#if !ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
Vec2d offset; // in unscaled coordinates
|
Vec2d offset; // in unscaled coordinates
|
||||||
|
#endif // !ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
|
||||||
// flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
|
// flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
|
||||||
EPrintVolumeState print_volume_state;
|
EPrintVolumeState print_volume_state;
|
||||||
|
|
||||||
ModelObject* get_object() const { return this->object; }
|
ModelObject* get_object() const { return this->object; }
|
||||||
|
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
const Vec3d& get_offset() const { return m_offset; }
|
||||||
|
double get_offset(Axis axis) const { return m_offset(axis); }
|
||||||
|
|
||||||
|
void set_offset(const Vec3d& offset) { m_offset = offset; }
|
||||||
|
void set_offset(Axis axis, double offset) { m_offset(axis) = offset; }
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
|
||||||
// To be called on an external mesh
|
// To be called on an external mesh
|
||||||
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
|
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
|
||||||
// Calculate a bounding box of a transformed mesh. To be called on an external mesh.
|
// Calculate a bounding box of a transformed mesh. To be called on an external mesh.
|
||||||
@ -253,9 +288,15 @@ private:
|
|||||||
// Parent object, owning this instance.
|
// Parent object, owning this instance.
|
||||||
ModelObject* object;
|
ModelObject* object;
|
||||||
|
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {}
|
||||||
|
ModelInstance(ModelObject *object, const ModelInstance &other) :
|
||||||
|
rotation(other.rotation), scaling_factor(other.scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {}
|
||||||
|
#else
|
||||||
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), offset(Vec2d::Zero()), object(object), print_volume_state(PVS_Inside) {}
|
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), offset(Vec2d::Zero()), object(object), print_volume_state(PVS_Inside) {}
|
||||||
ModelInstance(ModelObject *object, const ModelInstance &other) :
|
ModelInstance(ModelObject *object, const ModelInstance &other) :
|
||||||
rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {}
|
rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {}
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -130,6 +130,7 @@ objfunc(const PointImpl& bincenter,
|
|||||||
double norm, // A norming factor for physical dimensions
|
double norm, // A norming factor for physical dimensions
|
||||||
// a spatial index to quickly get neighbors of the candidate item
|
// a spatial index to quickly get neighbors of the candidate item
|
||||||
const SpatIndex& spatindex,
|
const SpatIndex& spatindex,
|
||||||
|
const SpatIndex& smalls_spatindex,
|
||||||
const ItemGroup& remaining
|
const ItemGroup& remaining
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -161,7 +162,7 @@ objfunc(const PointImpl& bincenter,
|
|||||||
// Will hold the resulting score
|
// Will hold the resulting score
|
||||||
double score = 0;
|
double score = 0;
|
||||||
|
|
||||||
if(isBig(item.area())) {
|
if(isBig(item.area()) || spatindex.empty()) {
|
||||||
// This branch is for the bigger items..
|
// This branch is for the bigger items..
|
||||||
|
|
||||||
auto minc = ibb.minCorner(); // bottom left corner
|
auto minc = ibb.minCorner(); // bottom left corner
|
||||||
@ -183,6 +184,8 @@ objfunc(const PointImpl& bincenter,
|
|||||||
|
|
||||||
// The smalles distance from the arranged pile center:
|
// The smalles distance from the arranged pile center:
|
||||||
auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
|
auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
|
||||||
|
auto bindist = pl::distance(ibb.center(), bincenter) / norm;
|
||||||
|
dist = 0.8*dist + 0.2*bindist;
|
||||||
|
|
||||||
// Density is the pack density: how big is the arranged pile
|
// Density is the pack density: how big is the arranged pile
|
||||||
double density = 0;
|
double density = 0;
|
||||||
@ -207,14 +210,20 @@ objfunc(const PointImpl& bincenter,
|
|||||||
// candidate to be aligned with only one item.
|
// candidate to be aligned with only one item.
|
||||||
auto alignment_score = 1.0;
|
auto alignment_score = 1.0;
|
||||||
|
|
||||||
density = (fullbb.width()*fullbb.height()) / (norm*norm);
|
density = std::sqrt((fullbb.width() / norm )*
|
||||||
|
(fullbb.height() / norm));
|
||||||
auto querybb = item.boundingBox();
|
auto querybb = item.boundingBox();
|
||||||
|
|
||||||
// Query the spatial index for the neighbors
|
// Query the spatial index for the neighbors
|
||||||
std::vector<SpatElement> result;
|
std::vector<SpatElement> result;
|
||||||
result.reserve(spatindex.size());
|
result.reserve(spatindex.size());
|
||||||
spatindex.query(bgi::intersects(querybb),
|
if(isBig(item.area())) {
|
||||||
std::back_inserter(result));
|
spatindex.query(bgi::intersects(querybb),
|
||||||
|
std::back_inserter(result));
|
||||||
|
} else {
|
||||||
|
smalls_spatindex.query(bgi::intersects(querybb),
|
||||||
|
std::back_inserter(result));
|
||||||
|
}
|
||||||
|
|
||||||
for(auto& e : result) { // now get the score for the best alignment
|
for(auto& e : result) { // now get the score for the best alignment
|
||||||
auto idx = e.second;
|
auto idx = e.second;
|
||||||
@ -235,12 +244,8 @@ objfunc(const PointImpl& bincenter,
|
|||||||
if(result.empty())
|
if(result.empty())
|
||||||
score = 0.5 * dist + 0.5 * density;
|
score = 0.5 * dist + 0.5 * density;
|
||||||
else
|
else
|
||||||
score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score;
|
score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score;
|
||||||
}
|
}
|
||||||
} else if( !isBig(item.area()) && spatindex.empty()) {
|
|
||||||
auto bindist = pl::distance(ibb.center(), bincenter) / norm;
|
|
||||||
// Bindist is surprisingly enough...
|
|
||||||
score = bindist;
|
|
||||||
} else {
|
} else {
|
||||||
// Here there are the small items that should be placed around the
|
// Here there are the small items that should be placed around the
|
||||||
// already processed bigger items.
|
// already processed bigger items.
|
||||||
@ -291,6 +296,7 @@ protected:
|
|||||||
PConfig pconf_; // Placement configuration
|
PConfig pconf_; // Placement configuration
|
||||||
double bin_area_;
|
double bin_area_;
|
||||||
SpatIndex rtree_;
|
SpatIndex rtree_;
|
||||||
|
SpatIndex smallsrtree_;
|
||||||
double norm_;
|
double norm_;
|
||||||
Pile merged_pile_;
|
Pile merged_pile_;
|
||||||
Box pilebb_;
|
Box pilebb_;
|
||||||
@ -299,7 +305,8 @@ protected:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
_ArrBase(const TBin& bin, Distance dist,
|
_ArrBase(const TBin& bin, Distance dist,
|
||||||
std::function<void(unsigned)> progressind):
|
std::function<void(unsigned)> progressind,
|
||||||
|
std::function<bool(void)> stopcond):
|
||||||
pck_(bin, dist), bin_area_(sl::area(bin)),
|
pck_(bin, dist), bin_area_(sl::area(bin)),
|
||||||
norm_(std::sqrt(sl::area(bin)))
|
norm_(std::sqrt(sl::area(bin)))
|
||||||
{
|
{
|
||||||
@ -317,6 +324,7 @@ public:
|
|||||||
pilebb_ = sl::boundingBox(merged_pile);
|
pilebb_ = sl::boundingBox(merged_pile);
|
||||||
|
|
||||||
rtree_.clear();
|
rtree_.clear();
|
||||||
|
smallsrtree_.clear();
|
||||||
|
|
||||||
// We will treat big items (compared to the print bed) differently
|
// We will treat big items (compared to the print bed) differently
|
||||||
auto isBig = [this](double a) {
|
auto isBig = [this](double a) {
|
||||||
@ -326,10 +334,12 @@ public:
|
|||||||
for(unsigned idx = 0; idx < items.size(); ++idx) {
|
for(unsigned idx = 0; idx < items.size(); ++idx) {
|
||||||
Item& itm = items[idx];
|
Item& itm = items[idx];
|
||||||
if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
|
if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
|
||||||
|
smallsrtree_.insert({itm.boundingBox(), idx});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pck_.progressIndicator(progressind);
|
pck_.progressIndicator(progressind);
|
||||||
|
pck_.stopCondition(stopcond);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
|
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
|
||||||
@ -343,8 +353,9 @@ class AutoArranger<Box>: public _ArrBase<Box> {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
AutoArranger(const Box& bin, Distance dist,
|
AutoArranger(const Box& bin, Distance dist,
|
||||||
std::function<void(unsigned)> progressind):
|
std::function<void(unsigned)> progressind,
|
||||||
_ArrBase<Box>(bin, dist, progressind)
|
std::function<bool(void)> stopcond):
|
||||||
|
_ArrBase<Box>(bin, dist, progressind, stopcond)
|
||||||
{
|
{
|
||||||
|
|
||||||
pconf_.object_function = [this, bin] (const Item &item) {
|
pconf_.object_function = [this, bin] (const Item &item) {
|
||||||
@ -357,6 +368,7 @@ public:
|
|||||||
bin_area_,
|
bin_area_,
|
||||||
norm_,
|
norm_,
|
||||||
rtree_,
|
rtree_,
|
||||||
|
smallsrtree_,
|
||||||
remaining_);
|
remaining_);
|
||||||
|
|
||||||
double score = std::get<0>(result);
|
double score = std::get<0>(result);
|
||||||
@ -380,8 +392,9 @@ class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
AutoArranger(const lnCircle& bin, Distance dist,
|
AutoArranger(const lnCircle& bin, Distance dist,
|
||||||
std::function<void(unsigned)> progressind):
|
std::function<void(unsigned)> progressind,
|
||||||
_ArrBase<lnCircle>(bin, dist, progressind) {
|
std::function<bool(void)> stopcond):
|
||||||
|
_ArrBase<lnCircle>(bin, dist, progressind, stopcond) {
|
||||||
|
|
||||||
pconf_.object_function = [this, &bin] (const Item &item) {
|
pconf_.object_function = [this, &bin] (const Item &item) {
|
||||||
|
|
||||||
@ -393,6 +406,7 @@ public:
|
|||||||
bin_area_,
|
bin_area_,
|
||||||
norm_,
|
norm_,
|
||||||
rtree_,
|
rtree_,
|
||||||
|
smallsrtree_,
|
||||||
remaining_);
|
remaining_);
|
||||||
|
|
||||||
double score = std::get<0>(result);
|
double score = std::get<0>(result);
|
||||||
@ -421,8 +435,9 @@ template<>
|
|||||||
class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
|
class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
|
||||||
public:
|
public:
|
||||||
AutoArranger(const PolygonImpl& bin, Distance dist,
|
AutoArranger(const PolygonImpl& bin, Distance dist,
|
||||||
std::function<void(unsigned)> progressind):
|
std::function<void(unsigned)> progressind,
|
||||||
_ArrBase<PolygonImpl>(bin, dist, progressind)
|
std::function<bool(void)> stopcond):
|
||||||
|
_ArrBase<PolygonImpl>(bin, dist, progressind, stopcond)
|
||||||
{
|
{
|
||||||
pconf_.object_function = [this, &bin] (const Item &item) {
|
pconf_.object_function = [this, &bin] (const Item &item) {
|
||||||
|
|
||||||
@ -435,6 +450,7 @@ public:
|
|||||||
bin_area_,
|
bin_area_,
|
||||||
norm_,
|
norm_,
|
||||||
rtree_,
|
rtree_,
|
||||||
|
smallsrtree_,
|
||||||
remaining_);
|
remaining_);
|
||||||
double score = std::get<0>(result);
|
double score = std::get<0>(result);
|
||||||
|
|
||||||
@ -449,8 +465,9 @@ template<> // Specialization with no bin
|
|||||||
class AutoArranger<bool>: public _ArrBase<Box> {
|
class AutoArranger<bool>: public _ArrBase<Box> {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
|
AutoArranger(Distance dist, std::function<void(unsigned)> progressind,
|
||||||
_ArrBase<Box>(Box(0, 0), dist, progressind)
|
std::function<bool(void)> stopcond):
|
||||||
|
_ArrBase<Box>(Box(0, 0), dist, progressind, stopcond)
|
||||||
{
|
{
|
||||||
this->pconf_.object_function = [this] (const Item &item) {
|
this->pconf_.object_function = [this] (const Item &item) {
|
||||||
|
|
||||||
@ -462,6 +479,7 @@ public:
|
|||||||
0,
|
0,
|
||||||
norm_,
|
norm_,
|
||||||
rtree_,
|
rtree_,
|
||||||
|
smallsrtree_,
|
||||||
remaining_);
|
remaining_);
|
||||||
return std::get<0>(result);
|
return std::get<0>(result);
|
||||||
};
|
};
|
||||||
@ -511,8 +529,13 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
|||||||
if(item.vertexCount() > 3) {
|
if(item.vertexCount() > 3) {
|
||||||
item.rotation(objinst->rotation);
|
item.rotation(objinst->rotation);
|
||||||
item.translation( {
|
item.translation( {
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
ClipperLib::cInt(objinst->get_offset(X) / SCALING_FACTOR),
|
||||||
|
ClipperLib::cInt(objinst->get_offset(Y) / SCALING_FACTOR)
|
||||||
|
#else
|
||||||
ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR),
|
ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR),
|
||||||
ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR)
|
ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR)
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
});
|
});
|
||||||
ret.emplace_back(objinst, item);
|
ret.emplace_back(objinst, item);
|
||||||
}
|
}
|
||||||
@ -649,11 +672,19 @@ void applyResult(
|
|||||||
// appropriately
|
// appropriately
|
||||||
auto off = item.translation();
|
auto off = item.translation();
|
||||||
Radians rot = item.rotation();
|
Radians rot = item.rotation();
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
Vec3d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR, 0.0);
|
||||||
|
#else
|
||||||
Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR);
|
Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
|
||||||
// write the tranformation data into the model instance
|
// write the tranformation data into the model instance
|
||||||
inst_ptr->rotation = rot;
|
inst_ptr->rotation = rot;
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
inst_ptr->set_offset(foff);
|
||||||
|
#else
|
||||||
inst_ptr->offset = foff;
|
inst_ptr->offset = foff;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,12 +711,16 @@ void applyResult(
|
|||||||
* remaining items which do not fit onto the print area next to the print
|
* remaining items which do not fit onto the print area next to the print
|
||||||
* bed or leave them untouched (let the user arrange them by hand or remove
|
* bed or leave them untouched (let the user arrange them by hand or remove
|
||||||
* them).
|
* them).
|
||||||
|
* \param progressind Progress indicator callback called when an object gets
|
||||||
|
* packed. The unsigned argument is the number of items remaining to pack.
|
||||||
|
* \param stopcondition A predicate returning true if abort is needed.
|
||||||
*/
|
*/
|
||||||
bool arrange(Model &model, coordf_t min_obj_distance,
|
bool arrange(Model &model, coordf_t min_obj_distance,
|
||||||
const Slic3r::Polyline& bed,
|
const Slic3r::Polyline& bed,
|
||||||
BedShapeHint bedhint,
|
BedShapeHint bedhint,
|
||||||
bool first_bin_only,
|
bool first_bin_only,
|
||||||
std::function<void(unsigned)> progressind)
|
std::function<void(unsigned)> progressind,
|
||||||
|
std::function<bool(void)> stopcondition)
|
||||||
{
|
{
|
||||||
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
|
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
|
||||||
|
|
||||||
@ -710,6 +745,8 @@ bool arrange(Model &model, coordf_t min_obj_distance,
|
|||||||
|
|
||||||
BoundingBox bbb(bed);
|
BoundingBox bbb(bed);
|
||||||
|
|
||||||
|
auto& cfn = stopcondition;
|
||||||
|
|
||||||
auto binbb = Box({
|
auto binbb = Box({
|
||||||
static_cast<libnest2d::Coord>(bbb.min(0)),
|
static_cast<libnest2d::Coord>(bbb.min(0)),
|
||||||
static_cast<libnest2d::Coord>(bbb.min(1))
|
static_cast<libnest2d::Coord>(bbb.min(1))
|
||||||
@ -723,7 +760,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
|
|||||||
case BedShapeType::BOX: {
|
case BedShapeType::BOX: {
|
||||||
|
|
||||||
// Create the arranger for the box shaped bed
|
// Create the arranger for the box shaped bed
|
||||||
AutoArranger<Box> arrange(binbb, min_obj_distance, progressind);
|
AutoArranger<Box> arrange(binbb, min_obj_distance, progressind, cfn);
|
||||||
|
|
||||||
// Arrange and return the items with their respective indices within the
|
// Arrange and return the items with their respective indices within the
|
||||||
// input sequence.
|
// input sequence.
|
||||||
@ -735,7 +772,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
|
|||||||
auto c = bedhint.shape.circ;
|
auto c = bedhint.shape.circ;
|
||||||
auto cc = lnCircle(c);
|
auto cc = lnCircle(c);
|
||||||
|
|
||||||
AutoArranger<lnCircle> arrange(cc, min_obj_distance, progressind);
|
AutoArranger<lnCircle> arrange(cc, min_obj_distance, progressind, cfn);
|
||||||
result = arrange(shapes.begin(), shapes.end());
|
result = arrange(shapes.begin(), shapes.end());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -747,7 +784,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
|
|||||||
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
||||||
P irrbed = sl::create<PolygonImpl>(std::move(ctour));
|
P irrbed = sl::create<PolygonImpl>(std::move(ctour));
|
||||||
|
|
||||||
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
|
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind, cfn);
|
||||||
|
|
||||||
// Arrange and return the items with their respective indices within the
|
// Arrange and return the items with their respective indices within the
|
||||||
// input sequence.
|
// input sequence.
|
||||||
@ -756,7 +793,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if(result.empty()) return false;
|
if(result.empty() || stopcondition()) return false;
|
||||||
|
|
||||||
if(first_bin_only) {
|
if(first_bin_only) {
|
||||||
applyResult(result.front(), 0, shapemap);
|
applyResult(result.front(), 0, shapemap);
|
||||||
|
@ -35,8 +35,10 @@ public:
|
|||||||
Point first_point() const;
|
Point first_point() const;
|
||||||
virtual Point last_point() const = 0;
|
virtual Point last_point() const = 0;
|
||||||
virtual Lines lines() const = 0;
|
virtual Lines lines() const = 0;
|
||||||
|
size_t size() const { return points.size(); }
|
||||||
|
bool empty() const { return points.empty(); }
|
||||||
double length() const;
|
double length() const;
|
||||||
bool is_valid() const { return this->points.size() >= 2; }
|
bool is_valid() const { return this->points.size() >= 2; }
|
||||||
|
|
||||||
int find_point(const Point &point) const;
|
int find_point(const Point &point) const;
|
||||||
bool has_boundary_point(const Point &point) const;
|
bool has_boundary_point(const Point &point) const;
|
||||||
|
@ -22,6 +22,7 @@ typedef Point Vector;
|
|||||||
// Vector types with a fixed point coordinate base type.
|
// Vector types with a fixed point coordinate base type.
|
||||||
typedef Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign> Vec2crd;
|
typedef Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign> Vec2crd;
|
||||||
typedef Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign> Vec3crd;
|
typedef Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign> Vec3crd;
|
||||||
|
typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> Vec3i;
|
||||||
typedef Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign> Vec2i64;
|
typedef Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign> Vec2i64;
|
||||||
typedef Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign> Vec3i64;
|
typedef Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign> Vec3i64;
|
||||||
|
|
||||||
|
@ -103,6 +103,12 @@ inline void polygons_rotate(Polygons &polys, double angle)
|
|||||||
p.rotate(cos_angle, sin_angle);
|
p.rotate(cos_angle, sin_angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void polygons_reverse(Polygons &polys)
|
||||||
|
{
|
||||||
|
for (Polygon &p : polys)
|
||||||
|
p.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
inline Points to_points(const Polygon &poly)
|
inline Points to_points(const Polygon &poly)
|
||||||
{
|
{
|
||||||
return poly.points;
|
return poly.points;
|
||||||
|
@ -184,15 +184,13 @@ void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
|
|||||||
|
|
||||||
bool Polyline::is_straight() const
|
bool Polyline::is_straight() const
|
||||||
{
|
{
|
||||||
/* Check that each segment's direction is equal to the line connecting
|
// Check that each segment's direction is equal to the line connecting
|
||||||
first point and last point. (Checking each line against the previous
|
// first point and last point. (Checking each line against the previous
|
||||||
one would cause the error to accumulate.) */
|
// one would cause the error to accumulate.)
|
||||||
double dir = Line(this->first_point(), this->last_point()).direction();
|
double dir = Line(this->first_point(), this->last_point()).direction();
|
||||||
|
for (const auto &line: this->lines())
|
||||||
Lines lines = this->lines();
|
if (! line.parallel_to(dir))
|
||||||
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
|
return false;
|
||||||
if (!line->parallel_to(dir)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,8 @@ public:
|
|||||||
Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {}
|
Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {}
|
||||||
Polyline(std::initializer_list<Point> list) : MultiPoint(list) {}
|
Polyline(std::initializer_list<Point> list) : MultiPoint(list) {}
|
||||||
explicit Polyline(const Point &p1, const Point &p2) { points.reserve(2); points.emplace_back(p1); points.emplace_back(p2); }
|
explicit Polyline(const Point &p1, const Point &p2) { points.reserve(2); points.emplace_back(p1); points.emplace_back(p2); }
|
||||||
|
explicit Polyline(const Points &points) : MultiPoint(points) {}
|
||||||
|
explicit Polyline(Points &&points) : MultiPoint(std::move(points)) {}
|
||||||
Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
|
Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
|
||||||
Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
|
Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
|
||||||
static Polyline new_scale(const std::vector<Vec2d> &points) {
|
static Polyline new_scale(const std::vector<Vec2d> &points) {
|
||||||
|
@ -392,9 +392,12 @@ void Print::add_model_object(ModelObject* model_object, int idx)
|
|||||||
//FIXME lock mutex!
|
//FIXME lock mutex!
|
||||||
this->invalidate_all_steps();
|
this->invalidate_all_steps();
|
||||||
|
|
||||||
for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
|
size_t volume_id = 0;
|
||||||
|
for (const ModelVolume *volume : model_object->volumes) {
|
||||||
|
if (! volume->is_model_part() && ! volume->is_modifier())
|
||||||
|
continue;
|
||||||
// Get the config applied to this volume.
|
// Get the config applied to this volume.
|
||||||
PrintRegionConfig config = this->_region_config_from_model_volume(*model_object->volumes[volume_id]);
|
PrintRegionConfig config = this->_region_config_from_model_volume(*volume);
|
||||||
// Find an existing print region with the same config.
|
// Find an existing print region with the same config.
|
||||||
size_t region_id = size_t(-1);
|
size_t region_id = size_t(-1);
|
||||||
for (size_t i = 0; i < m_regions.size(); ++ i)
|
for (size_t i = 0; i < m_regions.size(); ++ i)
|
||||||
@ -409,6 +412,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
|
|||||||
}
|
}
|
||||||
// Assign volume to a region.
|
// Assign volume to a region.
|
||||||
object->add_region_volume(region_id, volume_id);
|
object->add_region_volume(region_id, volume_id);
|
||||||
|
++ volume_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply config to print object.
|
// Apply config to print object.
|
||||||
@ -893,7 +897,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
|
|||||||
for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
|
for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
|
||||||
ModelVolume *volume = model_object->volumes[volume_id];
|
ModelVolume *volume = model_object->volumes[volume_id];
|
||||||
//FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
|
//FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
|
||||||
if (! volume->material_id().empty() && ! volume->config.has("extruder"))
|
if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder"))
|
||||||
volume->config.opt<ConfigOptionInt>("extruder", true)->value = int(volume_id + 1);
|
volume->config.opt<ConfigOptionInt>("extruder", true)->value = int(volume_id + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1295,6 +1299,9 @@ void Print::_make_wipe_tower()
|
|||||||
}
|
}
|
||||||
m_wipe_tower_data.final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
|
m_wipe_tower_data.final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
|
||||||
wipe_tower.tool_change((unsigned int)-1, false));
|
wipe_tower.tool_change((unsigned int)-1, false));
|
||||||
|
|
||||||
|
m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
|
||||||
|
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Print::output_filename() const
|
std::string Print::output_filename() const
|
||||||
|
@ -135,7 +135,10 @@ public:
|
|||||||
const Print* print() const { return m_print; }
|
const Print* print() const { return m_print; }
|
||||||
const PrintRegionConfig& config() const { return m_config; }
|
const PrintRegionConfig& config() const { return m_config; }
|
||||||
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
|
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
|
||||||
|
// Average diameter of nozzles participating on extruding this region.
|
||||||
coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
|
coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
|
||||||
|
// Average diameter of nozzles participating on extruding this region.
|
||||||
|
coordf_t bridging_height_avg(const PrintConfig &print_config) const;
|
||||||
|
|
||||||
// Methods modifying the PrintRegion's state:
|
// Methods modifying the PrintRegion's state:
|
||||||
public:
|
public:
|
||||||
@ -252,6 +255,10 @@ public:
|
|||||||
// Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t
|
// Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t
|
||||||
void slice();
|
void slice();
|
||||||
|
|
||||||
|
// Helpers to slice support enforcer / blocker meshes by the support generator.
|
||||||
|
std::vector<ExPolygons> slice_support_enforcers() const;
|
||||||
|
std::vector<ExPolygons> slice_support_blockers() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void make_perimeters();
|
void make_perimeters();
|
||||||
void prepare_infill();
|
void prepare_infill();
|
||||||
@ -297,6 +304,7 @@ private:
|
|||||||
void set_started(PrintObjectStep step);
|
void set_started(PrintObjectStep step);
|
||||||
void set_done(PrintObjectStep step);
|
void set_done(PrintObjectStep step);
|
||||||
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
|
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
|
||||||
|
std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WipeTowerData
|
struct WipeTowerData
|
||||||
@ -309,6 +317,8 @@ struct WipeTowerData
|
|||||||
std::unique_ptr<WipeTower::ToolChangeResult> priming;
|
std::unique_ptr<WipeTower::ToolChangeResult> priming;
|
||||||
std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
|
std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
|
||||||
std::unique_ptr<WipeTower::ToolChangeResult> final_purge;
|
std::unique_ptr<WipeTower::ToolChangeResult> final_purge;
|
||||||
|
std::vector<float> used_filament;
|
||||||
|
int number_of_toolchanges;
|
||||||
|
|
||||||
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
|
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
|
||||||
float depth;
|
float depth;
|
||||||
@ -318,6 +328,8 @@ struct WipeTowerData
|
|||||||
priming.reset(nullptr);
|
priming.reset(nullptr);
|
||||||
tool_changes.clear();
|
tool_changes.clear();
|
||||||
final_purge.reset(nullptr);
|
final_purge.reset(nullptr);
|
||||||
|
used_filament.clear();
|
||||||
|
number_of_toolchanges = -1;
|
||||||
depth = 0.f;
|
depth = 0.f;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -331,6 +343,8 @@ struct PrintStatistics
|
|||||||
double total_extruded_volume;
|
double total_extruded_volume;
|
||||||
double total_cost;
|
double total_cost;
|
||||||
double total_weight;
|
double total_weight;
|
||||||
|
double total_wipe_tower_cost;
|
||||||
|
double total_wipe_tower_filament;
|
||||||
std::map<size_t, float> filament_stats;
|
std::map<size_t, float> filament_stats;
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
@ -340,6 +354,8 @@ struct PrintStatistics
|
|||||||
total_extruded_volume = 0.;
|
total_extruded_volume = 0.;
|
||||||
total_cost = 0.;
|
total_cost = 0.;
|
||||||
total_weight = 0.;
|
total_weight = 0.;
|
||||||
|
total_wipe_tower_cost = 0.;
|
||||||
|
total_wipe_tower_filament = 0.;
|
||||||
filament_stats.clear();
|
filament_stats.clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -463,7 +479,7 @@ private:
|
|||||||
|
|
||||||
// If the background processing stop was requested, throw CanceledException.
|
// If the background processing stop was requested, throw CanceledException.
|
||||||
// To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
|
// To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
|
||||||
void throw_if_canceled() { if (m_canceled) throw CanceledException(); }
|
void throw_if_canceled() const { if (m_canceled) throw CanceledException(); }
|
||||||
|
|
||||||
void _make_skirt();
|
void _make_skirt();
|
||||||
void _make_brim();
|
void _make_brim();
|
||||||
|
@ -161,7 +161,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->tooltip = L("Speed for printing bridges.");
|
def->tooltip = L("Speed for printing bridges.");
|
||||||
def->sidetext = L("mm/s");
|
def->sidetext = L("mm/s");
|
||||||
def->cli = "bridge-speed=f";
|
def->cli = "bridge-speed=f";
|
||||||
def->aliases.push_back("bridge_feed_rate");
|
def->aliases = { "bridge_feed_rate" };
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->default_value = new ConfigOptionFloat(60);
|
def->default_value = new ConfigOptionFloat(60);
|
||||||
|
|
||||||
@ -274,7 +274,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->tooltip = L("Distance used for the auto-arrange feature of the plater.");
|
def->tooltip = L("Distance used for the auto-arrange feature of the plater.");
|
||||||
def->sidetext = L("mm");
|
def->sidetext = L("mm");
|
||||||
def->cli = "duplicate-distance=f";
|
def->cli = "duplicate-distance=f";
|
||||||
def->aliases.push_back("multiply_distance");
|
def->aliases = { "multiply_distance" };
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->default_value = new ConfigOptionFloat(6);
|
def->default_value = new ConfigOptionFloat(6);
|
||||||
|
|
||||||
@ -335,7 +335,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->enum_labels.push_back(L("Archimedean Chords"));
|
def->enum_labels.push_back(L("Archimedean Chords"));
|
||||||
def->enum_labels.push_back(L("Octagram Spiral"));
|
def->enum_labels.push_back(L("Octagram Spiral"));
|
||||||
// solid_fill_pattern is an obsolete equivalent to external_fill_pattern.
|
// solid_fill_pattern is an obsolete equivalent to external_fill_pattern.
|
||||||
def->aliases.push_back("solid_fill_pattern");
|
def->aliases = { "solid_fill_pattern" };
|
||||||
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
|
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
|
||||||
|
|
||||||
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
|
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
|
||||||
@ -923,8 +923,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->tooltip = L("Speed for printing the internal fill. Set to zero for auto.");
|
def->tooltip = L("Speed for printing the internal fill. Set to zero for auto.");
|
||||||
def->sidetext = L("mm/s");
|
def->sidetext = L("mm/s");
|
||||||
def->cli = "infill-speed=f";
|
def->cli = "infill-speed=f";
|
||||||
def->aliases.push_back("print_feed_rate");
|
def->aliases = { "print_feed_rate", "infill_feed_rate" };
|
||||||
def->aliases.push_back("infill_feed_rate");
|
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->default_value = new ConfigOptionFloat(80);
|
def->default_value = new ConfigOptionFloat(80);
|
||||||
|
|
||||||
@ -1272,7 +1271,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->category = L("Extruders");
|
def->category = L("Extruders");
|
||||||
def->tooltip = L("The extruder to use when printing perimeters and brim. First extruder is 1.");
|
def->tooltip = L("The extruder to use when printing perimeters and brim. First extruder is 1.");
|
||||||
def->cli = "perimeter-extruder=i";
|
def->cli = "perimeter-extruder=i";
|
||||||
def->aliases.push_back("perimeters_extruder");
|
def->aliases = { "perimeters_extruder" };
|
||||||
def->min = 1;
|
def->min = 1;
|
||||||
def->default_value = new ConfigOptionInt(1);
|
def->default_value = new ConfigOptionInt(1);
|
||||||
|
|
||||||
@ -1285,7 +1284,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
"If expressed as percentage (for example 200%) it will be computed over layer height.");
|
"If expressed as percentage (for example 200%) it will be computed over layer height.");
|
||||||
def->sidetext = L("mm or % (leave 0 for default)");
|
def->sidetext = L("mm or % (leave 0 for default)");
|
||||||
def->cli = "perimeter-extrusion-width=s";
|
def->cli = "perimeter-extrusion-width=s";
|
||||||
def->aliases.push_back("perimeters_extrusion_width");
|
def->aliases = { "perimeters_extrusion_width" };
|
||||||
def->default_value = new ConfigOptionFloatOrPercent(0, false);
|
def->default_value = new ConfigOptionFloatOrPercent(0, false);
|
||||||
|
|
||||||
def = this->add("perimeter_speed", coFloat);
|
def = this->add("perimeter_speed", coFloat);
|
||||||
@ -1294,7 +1293,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->tooltip = L("Speed for perimeters (contours, aka vertical shells). Set to zero for auto.");
|
def->tooltip = L("Speed for perimeters (contours, aka vertical shells). Set to zero for auto.");
|
||||||
def->sidetext = L("mm/s");
|
def->sidetext = L("mm/s");
|
||||||
def->cli = "perimeter-speed=f";
|
def->cli = "perimeter-speed=f";
|
||||||
def->aliases.push_back("perimeter_feed_rate");
|
def->aliases = { "perimeter_feed_rate" };
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->default_value = new ConfigOptionFloat(60);
|
def->default_value = new ConfigOptionFloat(60);
|
||||||
|
|
||||||
@ -1307,7 +1306,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
"if the Extra Perimeters option is enabled.");
|
"if the Extra Perimeters option is enabled.");
|
||||||
def->sidetext = L("(minimum)");
|
def->sidetext = L("(minimum)");
|
||||||
def->cli = "perimeters=i";
|
def->cli = "perimeters=i";
|
||||||
def->aliases.push_back("perimeter_offsets");
|
def->aliases = { "perimeter_offsets" };
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->default_value = new ConfigOptionInt(3);
|
def->default_value = new ConfigOptionInt(3);
|
||||||
|
|
||||||
@ -1635,7 +1634,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->sidetext = L("mm/s or %");
|
def->sidetext = L("mm/s or %");
|
||||||
def->cli = "solid-infill-speed=s";
|
def->cli = "solid-infill-speed=s";
|
||||||
def->ratio_over = "infill_speed";
|
def->ratio_over = "infill_speed";
|
||||||
def->aliases.push_back("solid_infill_feed_rate");
|
def->aliases = { "solid_infill_feed_rate" };
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->default_value = new ConfigOptionFloatOrPercent(20, false);
|
def->default_value = new ConfigOptionFloatOrPercent(20, false);
|
||||||
|
|
||||||
@ -1717,6 +1716,14 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->cli = "support-material!";
|
def->cli = "support-material!";
|
||||||
def->default_value = new ConfigOptionBool(false);
|
def->default_value = new ConfigOptionBool(false);
|
||||||
|
|
||||||
|
def = this->add("support_material_auto", coBool);
|
||||||
|
def->label = L("Auto generated supports");
|
||||||
|
def->category = L("Support material");
|
||||||
|
def->tooltip = L("If checked, supports will be generated automatically based on the overhang threshold value."\
|
||||||
|
" If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only.");
|
||||||
|
def->cli = "support-material-auto!";
|
||||||
|
def->default_value = new ConfigOptionBool(true);
|
||||||
|
|
||||||
def = this->add("support_material_xy_spacing", coFloatOrPercent);
|
def = this->add("support_material_xy_spacing", coFloatOrPercent);
|
||||||
def->label = L("XY separation between an object and its support");
|
def->label = L("XY separation between an object and its support");
|
||||||
def->category = L("Support material");
|
def->category = L("Support material");
|
||||||
@ -1755,7 +1762,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
"for the first object layer.");
|
"for the first object layer.");
|
||||||
def->sidetext = L("mm");
|
def->sidetext = L("mm");
|
||||||
def->cli = "support-material-contact-distance=f";
|
def->cli = "support-material-contact-distance=f";
|
||||||
def->min = 0;
|
// def->min = 0;
|
||||||
def->enum_values.push_back("0");
|
def->enum_values.push_back("0");
|
||||||
def->enum_values.push_back("0.2");
|
def->enum_values.push_back("0.2");
|
||||||
def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str());
|
def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str());
|
||||||
@ -1981,7 +1988,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->tooltip = L("Speed for travel moves (jumps between distant extrusion points).");
|
def->tooltip = L("Speed for travel moves (jumps between distant extrusion points).");
|
||||||
def->sidetext = L("mm/s");
|
def->sidetext = L("mm/s");
|
||||||
def->cli = "travel-speed=f";
|
def->cli = "travel-speed=f";
|
||||||
def->aliases.push_back("travel_feed_rate");
|
def->aliases = { "travel_feed_rate" };
|
||||||
def->min = 1;
|
def->min = 1;
|
||||||
def->default_value = new ConfigOptionFloat(130);
|
def->default_value = new ConfigOptionFloat(130);
|
||||||
|
|
||||||
|
@ -345,6 +345,7 @@ public:
|
|||||||
ConfigOptionFloatOrPercent extrusion_width;
|
ConfigOptionFloatOrPercent extrusion_width;
|
||||||
ConfigOptionFloatOrPercent first_layer_height;
|
ConfigOptionFloatOrPercent first_layer_height;
|
||||||
ConfigOptionBool infill_only_where_needed;
|
ConfigOptionBool infill_only_where_needed;
|
||||||
|
// Force the generation of solid shells between adjacent materials/volumes.
|
||||||
ConfigOptionBool interface_shells;
|
ConfigOptionBool interface_shells;
|
||||||
ConfigOptionFloat layer_height;
|
ConfigOptionFloat layer_height;
|
||||||
ConfigOptionInt raft_layers;
|
ConfigOptionInt raft_layers;
|
||||||
@ -352,6 +353,9 @@ public:
|
|||||||
// ConfigOptionFloat seam_preferred_direction;
|
// ConfigOptionFloat seam_preferred_direction;
|
||||||
// ConfigOptionFloat seam_preferred_direction_jitter;
|
// ConfigOptionFloat seam_preferred_direction_jitter;
|
||||||
ConfigOptionBool support_material;
|
ConfigOptionBool support_material;
|
||||||
|
// Automatic supports (generated based on support_material_threshold).
|
||||||
|
ConfigOptionBool support_material_auto;
|
||||||
|
// Direction of the support pattern (in XY plane).
|
||||||
ConfigOptionFloat support_material_angle;
|
ConfigOptionFloat support_material_angle;
|
||||||
ConfigOptionBool support_material_buildplate_only;
|
ConfigOptionBool support_material_buildplate_only;
|
||||||
ConfigOptionFloat support_material_contact_distance;
|
ConfigOptionFloat support_material_contact_distance;
|
||||||
@ -361,12 +365,15 @@ public:
|
|||||||
ConfigOptionBool support_material_interface_contact_loops;
|
ConfigOptionBool support_material_interface_contact_loops;
|
||||||
ConfigOptionInt support_material_interface_extruder;
|
ConfigOptionInt support_material_interface_extruder;
|
||||||
ConfigOptionInt support_material_interface_layers;
|
ConfigOptionInt support_material_interface_layers;
|
||||||
|
// Spacing between interface lines (the hatching distance). Set zero to get a solid interface.
|
||||||
ConfigOptionFloat support_material_interface_spacing;
|
ConfigOptionFloat support_material_interface_spacing;
|
||||||
ConfigOptionFloatOrPercent support_material_interface_speed;
|
ConfigOptionFloatOrPercent support_material_interface_speed;
|
||||||
ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
|
ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
|
||||||
|
// Spacing between support material lines (the hatching distance).
|
||||||
ConfigOptionFloat support_material_spacing;
|
ConfigOptionFloat support_material_spacing;
|
||||||
ConfigOptionFloat support_material_speed;
|
ConfigOptionFloat support_material_speed;
|
||||||
ConfigOptionBool support_material_synchronize_layers;
|
ConfigOptionBool support_material_synchronize_layers;
|
||||||
|
// Overhang angle threshold.
|
||||||
ConfigOptionInt support_material_threshold;
|
ConfigOptionInt support_material_threshold;
|
||||||
ConfigOptionBool support_material_with_sheath;
|
ConfigOptionBool support_material_with_sheath;
|
||||||
ConfigOptionFloatOrPercent support_material_xy_spacing;
|
ConfigOptionFloatOrPercent support_material_xy_spacing;
|
||||||
@ -389,6 +396,7 @@ protected:
|
|||||||
// OPT_PTR(seam_preferred_direction);
|
// OPT_PTR(seam_preferred_direction);
|
||||||
// OPT_PTR(seam_preferred_direction_jitter);
|
// OPT_PTR(seam_preferred_direction_jitter);
|
||||||
OPT_PTR(support_material);
|
OPT_PTR(support_material);
|
||||||
|
OPT_PTR(support_material_auto);
|
||||||
OPT_PTR(support_material_angle);
|
OPT_PTR(support_material_angle);
|
||||||
OPT_PTR(support_material_buildplate_only);
|
OPT_PTR(support_material_buildplate_only);
|
||||||
OPT_PTR(support_material_contact_distance);
|
OPT_PTR(support_material_contact_distance);
|
||||||
@ -436,10 +444,12 @@ public:
|
|||||||
ConfigOptionInt infill_every_layers;
|
ConfigOptionInt infill_every_layers;
|
||||||
ConfigOptionFloatOrPercent infill_overlap;
|
ConfigOptionFloatOrPercent infill_overlap;
|
||||||
ConfigOptionFloat infill_speed;
|
ConfigOptionFloat infill_speed;
|
||||||
|
// Detect bridging perimeters
|
||||||
ConfigOptionBool overhangs;
|
ConfigOptionBool overhangs;
|
||||||
ConfigOptionInt perimeter_extruder;
|
ConfigOptionInt perimeter_extruder;
|
||||||
ConfigOptionFloatOrPercent perimeter_extrusion_width;
|
ConfigOptionFloatOrPercent perimeter_extrusion_width;
|
||||||
ConfigOptionFloat perimeter_speed;
|
ConfigOptionFloat perimeter_speed;
|
||||||
|
// Total number of perimeters.
|
||||||
ConfigOptionInt perimeters;
|
ConfigOptionInt perimeters;
|
||||||
ConfigOptionFloatOrPercent small_perimeter_speed;
|
ConfigOptionFloatOrPercent small_perimeter_speed;
|
||||||
ConfigOptionFloat solid_infill_below_area;
|
ConfigOptionFloat solid_infill_below_area;
|
||||||
@ -447,6 +457,7 @@ public:
|
|||||||
ConfigOptionFloatOrPercent solid_infill_extrusion_width;
|
ConfigOptionFloatOrPercent solid_infill_extrusion_width;
|
||||||
ConfigOptionInt solid_infill_every_layers;
|
ConfigOptionInt solid_infill_every_layers;
|
||||||
ConfigOptionFloatOrPercent solid_infill_speed;
|
ConfigOptionFloatOrPercent solid_infill_speed;
|
||||||
|
// Detect thin walls.
|
||||||
ConfigOptionBool thin_walls;
|
ConfigOptionBool thin_walls;
|
||||||
ConfigOptionFloatOrPercent top_infill_extrusion_width;
|
ConfigOptionFloatOrPercent top_infill_extrusion_width;
|
||||||
ConfigOptionInt top_solid_layers;
|
ConfigOptionInt top_solid_layers;
|
||||||
|
@ -112,8 +112,18 @@ bool PrintObject::reload_model_instances()
|
|||||||
Points copies;
|
Points copies;
|
||||||
copies.reserve(m_model_object->instances.size());
|
copies.reserve(m_model_object->instances.size());
|
||||||
for (const ModelInstance *mi : m_model_object->instances)
|
for (const ModelInstance *mi : m_model_object->instances)
|
||||||
|
{
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
if (mi->is_printable())
|
||||||
|
{
|
||||||
|
const Vec3d& offset = mi->get_offset();
|
||||||
|
copies.emplace_back(Point::new_scale(offset(0), offset(1)));
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (mi->is_printable())
|
if (mi->is_printable())
|
||||||
copies.emplace_back(Point::new_scale(mi->offset(0), mi->offset(1)));
|
copies.emplace_back(Point::new_scale(mi->offset(0), mi->offset(1)));
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
}
|
||||||
return this->set_copies(copies);
|
return this->set_copies(copies);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,6 +500,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||||||
steps.emplace_back(posSlice);
|
steps.emplace_back(posSlice);
|
||||||
} else if (
|
} else if (
|
||||||
opt_key == "support_material"
|
opt_key == "support_material"
|
||||||
|
|| opt_key == "support_material_auto"
|
||||||
|| opt_key == "support_material_angle"
|
|| opt_key == "support_material_angle"
|
||||||
|| opt_key == "support_material_buildplate_only"
|
|| opt_key == "support_material_buildplate_only"
|
||||||
|| opt_key == "support_material_enforce_layers"
|
|| opt_key == "support_material_enforce_layers"
|
||||||
@ -1560,34 +1571,66 @@ end:
|
|||||||
|
|
||||||
std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
|
std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
|
||||||
{
|
{
|
||||||
std::vector<ExPolygons> layers;
|
std::vector<const ModelVolume*> volumes;
|
||||||
if (region_id < this->region_volumes.size()) {
|
if (region_id < this->region_volumes.size()) {
|
||||||
std::vector<int> &volumes = this->region_volumes[region_id];
|
for (int volume_id : this->region_volumes[region_id]) {
|
||||||
if (! volumes.empty()) {
|
const ModelVolume *volume = this->model_object()->volumes[volume_id];
|
||||||
// Compose mesh.
|
if (modifier ? volume->is_modifier() : volume->is_model_part())
|
||||||
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
|
volumes.emplace_back(volume);
|
||||||
TriangleMesh mesh;
|
}
|
||||||
for (int volume_id : volumes) {
|
}
|
||||||
ModelVolume *volume = this->model_object()->volumes[volume_id];
|
return this->_slice_volumes(z, volumes);
|
||||||
if (volume->modifier == modifier)
|
}
|
||||||
mesh.merge(volume->mesh);
|
|
||||||
m_print->throw_if_canceled();
|
std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
|
||||||
}
|
{
|
||||||
if (mesh.stl.stats.number_of_facets > 0) {
|
std::vector<const ModelVolume*> volumes;
|
||||||
// transform mesh
|
for (const ModelVolume *volume : this->model_object()->volumes)
|
||||||
// we ignore the per-instance transformations currently and only
|
if (volume->is_support_enforcer())
|
||||||
// consider the first one
|
volumes.emplace_back(volume);
|
||||||
this->model_object()->instances.front()->transform_mesh(&mesh, true);
|
std::vector<float> zs;
|
||||||
// align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
|
zs.reserve(this->layers().size());
|
||||||
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), - float(this->model_object()->bounding_box().min(2)));
|
for (const Layer *l : this->layers())
|
||||||
// perform actual slicing
|
zs.emplace_back(l->slice_z);
|
||||||
TriangleMeshSlicer mslicer;
|
return this->_slice_volumes(zs, volumes);
|
||||||
Print *print = this->print();
|
}
|
||||||
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
|
|
||||||
mslicer.init(&mesh, callback);
|
std::vector<ExPolygons> PrintObject::slice_support_blockers() const
|
||||||
mslicer.slice(z, &layers, callback);
|
{
|
||||||
m_print->throw_if_canceled();
|
std::vector<const ModelVolume*> volumes;
|
||||||
}
|
for (const ModelVolume *volume : this->model_object()->volumes)
|
||||||
|
if (volume->is_support_blocker())
|
||||||
|
volumes.emplace_back(volume);
|
||||||
|
std::vector<float> zs;
|
||||||
|
zs.reserve(this->layers().size());
|
||||||
|
for (const Layer *l : this->layers())
|
||||||
|
zs.emplace_back(l->slice_z);
|
||||||
|
return this->_slice_volumes(zs, volumes);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
|
||||||
|
{
|
||||||
|
std::vector<ExPolygons> layers;
|
||||||
|
if (! volumes.empty()) {
|
||||||
|
// Compose mesh.
|
||||||
|
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
|
||||||
|
TriangleMesh mesh;
|
||||||
|
for (const ModelVolume *v : volumes)
|
||||||
|
mesh.merge(v->mesh);
|
||||||
|
if (mesh.stl.stats.number_of_facets > 0) {
|
||||||
|
// transform mesh
|
||||||
|
// we ignore the per-instance transformations currently and only
|
||||||
|
// consider the first one
|
||||||
|
this->model_object()->instances.front()->transform_mesh(&mesh, true);
|
||||||
|
// align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
|
||||||
|
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), - float(this->model_object()->bounding_box().min(2)));
|
||||||
|
// perform actual slicing
|
||||||
|
TriangleMeshSlicer mslicer;
|
||||||
|
const Print *print = this->print();
|
||||||
|
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
|
||||||
|
mslicer.init(&mesh, callback);
|
||||||
|
mslicer.slice(z, &layers, callback);
|
||||||
|
m_print->throw_if_canceled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return layers;
|
return layers;
|
||||||
|
@ -56,4 +56,9 @@ coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
|
|||||||
print_config.nozzle_diameter.get_at(m_config.solid_infill_extruder.value - 1)) / 3.;
|
print_config.nozzle_diameter.get_at(m_config.solid_infill_extruder.value - 1)) / 3.;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
|
||||||
|
{
|
||||||
|
return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -224,9 +224,9 @@ std::vector<coordf_t> layer_height_profile_adaptive(
|
|||||||
// 1) Initialize the SlicingAdaptive class with the object meshes.
|
// 1) Initialize the SlicingAdaptive class with the object meshes.
|
||||||
SlicingAdaptive as;
|
SlicingAdaptive as;
|
||||||
as.set_slicing_parameters(slicing_params);
|
as.set_slicing_parameters(slicing_params);
|
||||||
for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it)
|
for (const ModelVolume *volume : volumes)
|
||||||
if (! (*it)->modifier)
|
if (volume->is_model_part())
|
||||||
as.add_mesh(&(*it)->mesh);
|
as.add_mesh(&volume->mesh);
|
||||||
as.prepare();
|
as.prepare();
|
||||||
|
|
||||||
// 2) Generate layers using the algorithm of @platsch
|
// 2) Generate layers using the algorithm of @platsch
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@ class PrintConfig;
|
|||||||
class PrintObjectConfig;
|
class PrintObjectConfig;
|
||||||
|
|
||||||
// how much we extend support around the actual contact area
|
// how much we extend support around the actual contact area
|
||||||
|
//FIXME this should be dependent on the nozzle diameter!
|
||||||
#define SUPPORT_MATERIAL_MARGIN 1.5
|
#define SUPPORT_MATERIAL_MARGIN 1.5
|
||||||
|
|
||||||
// This class manages raft and supports for a single PrintObject.
|
// This class manages raft and supports for a single PrintObject.
|
||||||
@ -71,6 +72,21 @@ public:
|
|||||||
overhang_polygons = nullptr;
|
overhang_polygons = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
layer_type = sltUnknown;
|
||||||
|
print_z = 0.;
|
||||||
|
bottom_z = 0.;
|
||||||
|
height = 0.;
|
||||||
|
idx_object_layer_above = size_t(-1);
|
||||||
|
idx_object_layer_below = size_t(-1);
|
||||||
|
bridging = false;
|
||||||
|
polygons.clear();
|
||||||
|
delete contact_polygons;
|
||||||
|
contact_polygons = nullptr;
|
||||||
|
delete overhang_polygons;
|
||||||
|
overhang_polygons = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool operator==(const MyLayer &layer2) const {
|
bool operator==(const MyLayer &layer2) const {
|
||||||
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
|
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,11 @@ public:
|
|||||||
|
|
||||||
void clear() { surfaces.clear(); }
|
void clear() { surfaces.clear(); }
|
||||||
bool empty() const { return surfaces.empty(); }
|
bool empty() const { return surfaces.empty(); }
|
||||||
|
bool has(SurfaceType type) const {
|
||||||
|
for (const Surface &surface : this->surfaces)
|
||||||
|
if (surface.surface_type == type) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
|
void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
|
||||||
void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
|
void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
|
||||||
|
12
xs/src/libslic3r/Technologies.hpp
Normal file
12
xs/src/libslic3r/Technologies.hpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef _technologies_h_
|
||||||
|
#define _technologies_h_
|
||||||
|
|
||||||
|
// 1.42.0 techs
|
||||||
|
#define ENABLE_1_42_0 1
|
||||||
|
|
||||||
|
// Add z coordinate to model instances' offset
|
||||||
|
#define ENABLE_MODELINSTANCE_3D_OFFSET (1 && ENABLE_1_42_0)
|
||||||
|
|
||||||
|
#endif // _technologies_h_
|
||||||
|
|
||||||
|
|
@ -21,16 +21,20 @@
|
|||||||
|
|
||||||
#include <Eigen/Dense>
|
#include <Eigen/Dense>
|
||||||
|
|
||||||
|
// for SLIC3R_DEBUG_SLICE_PROCESSING
|
||||||
|
#include "libslic3r.h"
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
#define DEBUG
|
#define DEBUG
|
||||||
#define _DEBUG
|
#define _DEBUG
|
||||||
#undef NDEBUG
|
#undef NDEBUG
|
||||||
|
#define SLIC3R_DEBUG
|
||||||
|
// #define SLIC3R_TRIANGLEMESH_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG
|
#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
|
||||||
// #define SLIC3R_TRIANGLEMESH_DEBUG
|
|
||||||
#include "SVG.hpp"
|
#include "SVG.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -156,7 +160,6 @@ void TriangleMesh::repair()
|
|||||||
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
|
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float TriangleMesh::volume()
|
float TriangleMesh::volume()
|
||||||
{
|
{
|
||||||
if (this->stl.stats.volume == -1)
|
if (this->stl.stats.volume == -1)
|
||||||
@ -320,7 +323,7 @@ bool TriangleMesh::has_multiple_patches() const
|
|||||||
facet_visited[facet_idx] = true;
|
facet_visited[facet_idx] = true;
|
||||||
for (int j = 0; j < 3; ++ j) {
|
for (int j = 0; j < 3; ++ j) {
|
||||||
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
|
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
|
||||||
if (! facet_visited[neighbor_idx])
|
if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
|
||||||
facet_queue[facet_queue_cnt ++] = neighbor_idx;
|
facet_queue[facet_queue_cnt ++] = neighbor_idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,7 +366,7 @@ size_t TriangleMesh::number_of_patches() const
|
|||||||
facet_visited[facet_idx] = true;
|
facet_visited[facet_idx] = true;
|
||||||
for (int j = 0; j < 3; ++ j) {
|
for (int j = 0; j < 3; ++ j) {
|
||||||
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
|
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
|
||||||
if (! facet_visited[neighbor_idx])
|
if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
|
||||||
facet_queue[facet_queue_cnt ++] = neighbor_idx;
|
facet_queue[facet_queue_cnt ++] = neighbor_idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -623,6 +626,20 @@ void TriangleMesh::require_shared_vertices()
|
|||||||
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
|
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
|
||||||
stl_generate_shared_vertices(&(this->stl));
|
stl_generate_shared_vertices(&(this->stl));
|
||||||
}
|
}
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// Verify validity of neighborship data.
|
||||||
|
for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) {
|
||||||
|
const stl_neighbors &nbr = stl.neighbors_start[facet_idx];
|
||||||
|
const int *vertices = stl.v_indices[facet_idx].vertex;
|
||||||
|
for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) {
|
||||||
|
int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx];
|
||||||
|
if (nbr_face != -1) {
|
||||||
|
assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3]);
|
||||||
|
assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* _DEBUG */
|
||||||
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
|
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,13 +716,13 @@ void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assign an edge index to the 1st face.
|
// Assign an edge index to the 1st face.
|
||||||
this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges;
|
this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges;
|
||||||
if (found) {
|
if (found) {
|
||||||
EdgeToFace &edge_j = edges_map[j];
|
EdgeToFace &edge_j = edges_map[j];
|
||||||
this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges;
|
this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges;
|
||||||
// Mark the edge as connected.
|
// Mark the edge as connected.
|
||||||
edge_j.face = -1;
|
edge_j.face = -1;
|
||||||
}
|
}
|
||||||
++ num_edges;
|
++ num_edges;
|
||||||
if ((i & 0x0ffff) == 0)
|
if ((i & 0x0ffff) == 0)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
@ -781,13 +798,30 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
|
|||||||
{
|
{
|
||||||
static int iRun = 0;
|
static int iRun = 0;
|
||||||
for (size_t i = 0; i < z.size(); ++ i) {
|
for (size_t i = 0; i < z.size(); ++ i) {
|
||||||
Polygons &polygons = (*layers)[i];
|
Polygons &polygons = (*layers)[i];
|
||||||
SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), union_ex(polygons, true));
|
ExPolygons expolygons = union_ex(polygons, true);
|
||||||
|
SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), expolygons);
|
||||||
|
{
|
||||||
|
BoundingBox bbox;
|
||||||
|
for (const IntersectionLine &l : lines[i]) {
|
||||||
|
bbox.merge(l.a);
|
||||||
|
bbox.merge(l.b);
|
||||||
|
}
|
||||||
|
SVG svg(debug_out_path("slice_loops_%d_%d.svg", iRun, i).c_str(), bbox);
|
||||||
|
svg.draw(expolygons);
|
||||||
|
for (const IntersectionLine &l : lines[i])
|
||||||
|
svg.draw(l, "red", 0);
|
||||||
|
svg.draw_outline(expolygons, "black", "blue", 0);
|
||||||
|
svg.Close();
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
|
||||||
for (Polygon &poly : polygons) {
|
for (Polygon &poly : polygons) {
|
||||||
for (size_t i = 1; i < poly.points.size(); ++ i)
|
for (size_t i = 1; i < poly.points.size(); ++ i)
|
||||||
assert(poly.points[i-1] != poly.points[i]);
|
assert(poly.points[i-1] != poly.points[i]);
|
||||||
assert(poly.points.front() != poly.points.back());
|
assert(poly.points.front() != poly.points.back());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
++ iRun;
|
++ iRun;
|
||||||
}
|
}
|
||||||
@ -803,46 +837,51 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
|
|||||||
const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2)));
|
const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2)));
|
||||||
const float max_z = fmaxf(facet.vertex[0](2), fmaxf(facet.vertex[1](2), facet.vertex[2](2)));
|
const float max_z = fmaxf(facet.vertex[0](2), fmaxf(facet.vertex[1](2), facet.vertex[2](2)));
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG
|
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||||
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
|
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
|
||||||
facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0](2),
|
facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0](2),
|
||||||
facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1](2),
|
facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1](2),
|
||||||
facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2](2));
|
facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2](2));
|
||||||
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
|
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
|
||||||
#endif
|
#endif /* SLIC3R_TRIANGLEMESH_DEBUG */
|
||||||
|
|
||||||
// find layer extents
|
// find layer extents
|
||||||
std::vector<float>::const_iterator min_layer, max_layer;
|
std::vector<float>::const_iterator min_layer, max_layer;
|
||||||
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
|
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
|
||||||
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
|
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
|
||||||
#ifdef SLIC3R_DEBUG
|
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||||
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
|
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
|
||||||
#endif
|
#endif /* SLIC3R_TRIANGLEMESH_DEBUG */
|
||||||
|
|
||||||
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
|
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
|
||||||
std::vector<float>::size_type layer_idx = it - z.begin();
|
std::vector<float>::size_type layer_idx = it - z.begin();
|
||||||
IntersectionLine il;
|
IntersectionLine il;
|
||||||
if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il)) {
|
if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il) == TriangleMeshSlicer::Slicing) {
|
||||||
boost::lock_guard<boost::mutex> l(*lines_mutex);
|
boost::lock_guard<boost::mutex> l(*lines_mutex);
|
||||||
if (il.edge_type == feHorizontal) {
|
if (il.edge_type == feHorizontal) {
|
||||||
// Insert all three edges of the face.
|
// Insert all marked edges of the face. The marked edges do not share an edge with another horizontal face
|
||||||
|
// (they may not have a nighbor, or their neighbor is vertical)
|
||||||
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
|
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
|
||||||
const bool reverse = this->mesh->stl.facet_start[facet_idx].normal(2) < 0;
|
const bool reverse = this->mesh->stl.facet_start[facet_idx].normal(2) < 0;
|
||||||
for (int j = 0; j < 3; ++ j) {
|
for (int j = 0; j < 3; ++ j)
|
||||||
int a_id = vertices[j % 3];
|
if (il.flags & ((IntersectionLine::EDGE0_NO_NEIGHBOR | IntersectionLine::EDGE0_FOLD) << j)) {
|
||||||
int b_id = vertices[(j+1) % 3];
|
int a_id = vertices[j % 3];
|
||||||
if (reverse)
|
int b_id = vertices[(j+1) % 3];
|
||||||
std::swap(a_id, b_id);
|
if (reverse)
|
||||||
const stl_vertex &a = this->v_scaled_shared[a_id];
|
std::swap(a_id, b_id);
|
||||||
const stl_vertex &b = this->v_scaled_shared[b_id];
|
const stl_vertex &a = this->v_scaled_shared[a_id];
|
||||||
il.a(0) = a(0);
|
const stl_vertex &b = this->v_scaled_shared[b_id];
|
||||||
il.a(1) = a(1);
|
il.a(0) = a(0);
|
||||||
il.b(0) = b(0);
|
il.a(1) = a(1);
|
||||||
il.b(1) = b(1);
|
il.b(0) = b(0);
|
||||||
il.a_id = a_id;
|
il.b(1) = b(1);
|
||||||
il.b_id = b_id;
|
il.a_id = a_id;
|
||||||
(*lines)[layer_idx].emplace_back(il);
|
il.b_id = b_id;
|
||||||
}
|
assert(il.a != il.b);
|
||||||
|
// This edge will not be used as a seed for loop extraction if it was added due to a fold of two overlapping horizontal faces.
|
||||||
|
il.set_no_seed((IntersectionLine::EDGE0_FOLD << j) != 0);
|
||||||
|
(*lines)[layer_idx].emplace_back(il);
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
(*lines)[layer_idx].emplace_back(il);
|
(*lines)[layer_idx].emplace_back(il);
|
||||||
}
|
}
|
||||||
@ -861,7 +900,7 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygo
|
|||||||
[&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
|
[&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
|
||||||
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
|
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
|
||||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||||
printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
|
printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
|
||||||
#endif
|
#endif
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
|
this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
|
||||||
@ -871,23 +910,22 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return true, if the facet has been sliced and line_out has been filled.
|
// Return true, if the facet has been sliced and line_out has been filled.
|
||||||
bool TriangleMeshSlicer::slice_facet(
|
TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
|
||||||
float slice_z, const stl_facet &facet, const int facet_idx,
|
float slice_z, const stl_facet &facet, const int facet_idx,
|
||||||
const float min_z, const float max_z,
|
const float min_z, const float max_z,
|
||||||
IntersectionLine *line_out) const
|
IntersectionLine *line_out) const
|
||||||
{
|
{
|
||||||
IntersectionPoint points[3];
|
IntersectionPoint points[3];
|
||||||
size_t num_points = 0;
|
size_t num_points = 0;
|
||||||
size_t points_on_layer[3];
|
size_t point_on_layer = size_t(-1);
|
||||||
size_t num_points_on_layer = 0;
|
|
||||||
|
|
||||||
// Reorder vertices so that the first one is the one with lowest Z.
|
// Reorder vertices so that the first one is the one with lowest Z.
|
||||||
// This is needed to get all intersection lines in a consistent order
|
// This is needed to get all intersection lines in a consistent order
|
||||||
// (external on the right of the line)
|
// (external on the right of the line)
|
||||||
|
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
|
||||||
int i = (facet.vertex[1](2) == min_z) ? 1 : ((facet.vertex[2](2) == min_z) ? 2 : 0);
|
int i = (facet.vertex[1](2) == min_z) ? 1 : ((facet.vertex[2](2) == min_z) ? 2 : 0);
|
||||||
for (int j = i; j - i < 3; ++ j) { // loop through facet edges
|
for (int j = i; j - i < 3; ++j ) { // loop through facet edges
|
||||||
int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)];
|
int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)];
|
||||||
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
|
|
||||||
int a_id = vertices[j % 3];
|
int a_id = vertices[j % 3];
|
||||||
int b_id = vertices[(j+1) % 3];
|
int b_id = vertices[(j+1) % 3];
|
||||||
const stl_vertex &a = this->v_scaled_shared[a_id];
|
const stl_vertex &a = this->v_scaled_shared[a_id];
|
||||||
@ -900,116 +938,279 @@ bool TriangleMeshSlicer::slice_facet(
|
|||||||
const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
|
const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
|
||||||
const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
|
const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
|
||||||
bool swap = false;
|
bool swap = false;
|
||||||
|
const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal;
|
||||||
|
// We may ignore this edge for slicing purposes, but we may still use it for object cutting.
|
||||||
|
FacetSliceType result = Slicing;
|
||||||
|
const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx];
|
||||||
if (min_z == max_z) {
|
if (min_z == max_z) {
|
||||||
// All three vertices are aligned with slice_z.
|
// All three vertices are aligned with slice_z.
|
||||||
line_out->edge_type = feHorizontal;
|
line_out->edge_type = feHorizontal;
|
||||||
if (this->mesh->stl.facet_start[facet_idx].normal(2) < 0) {
|
// Mark neighbor edges, which do not have a neighbor.
|
||||||
|
uint32_t edges = 0;
|
||||||
|
for (int nbr_idx = 0; nbr_idx != 3; ++ nbr_idx) {
|
||||||
|
// If the neighbor with an edge starting with a vertex idx (nbr_idx - 2) shares no
|
||||||
|
// opposite face, add it to the edges to process when slicing.
|
||||||
|
if (nbr.neighbor[nbr_idx] == -1) {
|
||||||
|
// Mark this edge to be added to the slice.
|
||||||
|
edges |= (IntersectionLine::EDGE0_NO_NEIGHBOR << nbr_idx);
|
||||||
|
}
|
||||||
|
#if 1
|
||||||
|
else if (normal(2) > 0) {
|
||||||
|
// Produce edges for opposite faced overlapping horizontal faces aka folds.
|
||||||
|
// This method often produces connecting lines (noise) at the cutting plane.
|
||||||
|
// Produce the edges for the top facing face of the pair of top / bottom facing faces.
|
||||||
|
|
||||||
|
// Index of a neighbor face.
|
||||||
|
const int nbr_face = nbr.neighbor[nbr_idx];
|
||||||
|
const int *nbr_vertices = this->mesh->stl.v_indices[nbr_face].vertex;
|
||||||
|
int idx_vertex_opposite = nbr_vertices[nbr.which_vertex_not[nbr_idx]];
|
||||||
|
const stl_vertex &c2 = this->v_scaled_shared[idx_vertex_opposite];
|
||||||
|
if (c2(2) == slice_z) {
|
||||||
|
// Edge shared by facet_idx and nbr_face.
|
||||||
|
int a_id = vertices[nbr_idx];
|
||||||
|
int b_id = vertices[(nbr_idx + 1) % 3];
|
||||||
|
int c1_id = vertices[(nbr_idx + 2) % 3];
|
||||||
|
const stl_vertex &a = this->v_scaled_shared[a_id];
|
||||||
|
const stl_vertex &b = this->v_scaled_shared[b_id];
|
||||||
|
const stl_vertex &c1 = this->v_scaled_shared[c1_id];
|
||||||
|
// Verify that the two neighbor faces share a common edge.
|
||||||
|
assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
|
||||||
|
assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
|
||||||
|
double n1 = (double(c1(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c1(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
|
||||||
|
double n2 = (double(c2(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c2(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
|
||||||
|
if (n1 * n2 > 0)
|
||||||
|
// The two faces overlap. This indicates an invalid mesh geometry (non-manifold),
|
||||||
|
// but these are the real world objects, and leaving out these edges leads to missing contours.
|
||||||
|
edges |= (IntersectionLine::EDGE0_FOLD << nbr_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
// Use some edges of this triangle for slicing only if at least one of its edge does not have an opposite face.
|
||||||
|
result = (edges == 0) ? Cutting : Slicing;
|
||||||
|
line_out->flags |= edges;
|
||||||
|
if (normal(2) < 0) {
|
||||||
// If normal points downwards this is a bottom horizontal facet so we reverse its point order.
|
// If normal points downwards this is a bottom horizontal facet so we reverse its point order.
|
||||||
swap = true;
|
swap = true;
|
||||||
}
|
}
|
||||||
} else if (v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z) {
|
|
||||||
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
|
|
||||||
line_out->edge_type = feTop;
|
|
||||||
swap = true;
|
|
||||||
} else {
|
} else {
|
||||||
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
|
// Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
|
||||||
line_out->edge_type = feBottom;
|
int nbr_idx = j % 3;
|
||||||
|
int nbr_face = nbr.neighbor[nbr_idx];
|
||||||
|
// Is the third vertex below the cutting plane?
|
||||||
|
bool third_below = v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z;
|
||||||
|
// Is this a concave corner?
|
||||||
|
if (nbr_face == -1) {
|
||||||
|
#ifdef _DEBUG
|
||||||
|
printf("Face has no neighbor!\n");
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
|
||||||
|
assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
|
||||||
|
int idx_vertex_opposite = this->mesh->stl.v_indices[nbr_face].vertex[nbr.which_vertex_not[nbr_idx]];
|
||||||
|
const stl_vertex &c = this->v_scaled_shared[idx_vertex_opposite];
|
||||||
|
if (c(2) == slice_z) {
|
||||||
|
double normal_nbr = (double(c(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
|
||||||
|
#if 0
|
||||||
|
if ((normal_nbr < 0) == third_below) {
|
||||||
|
printf("Flipped normal?\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
result =
|
||||||
|
// A vertical face shares edge with a horizontal face. Verify, whether the shared edge makes a convex or concave corner.
|
||||||
|
// Unfortunately too often there are flipped normals, which brake our assumption. Let's rather return every edge,
|
||||||
|
// and leth the code downstream hopefully handle it.
|
||||||
|
#if 1
|
||||||
|
// Ignore concave corners for slicing.
|
||||||
|
// This method has the unfortunate property, that folds in a horizontal plane create concave corners,
|
||||||
|
// leading to broken contours, if these concave corners are not replaced by edges of the folds, see above.
|
||||||
|
((normal_nbr < 0) == third_below) ? Cutting : Slicing;
|
||||||
|
#else
|
||||||
|
// Use concave corners for slicing. This leads to the test 01_trianglemesh.t "slicing a top tangent plane includes its area" failing,
|
||||||
|
// and rightly so.
|
||||||
|
Slicing;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
// For a pair of faces touching exactly at the cutting plane, ignore one of them. An arbitrary rule is to ignore the face with a higher index.
|
||||||
|
result = (facet_idx < nbr_face) ? Slicing : Cutting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (third_below) {
|
||||||
|
line_out->edge_type = feTop;
|
||||||
|
swap = true;
|
||||||
|
} else
|
||||||
|
line_out->edge_type = feBottom;
|
||||||
}
|
}
|
||||||
line_out->a = to_2d(swap ? b : a).cast<coord_t>();
|
line_out->a = to_2d(swap ? b : a).cast<coord_t>();
|
||||||
line_out->b = to_2d(swap ? a : b).cast<coord_t>();
|
line_out->b = to_2d(swap ? a : b).cast<coord_t>();
|
||||||
line_out->a_id = swap ? b_id : a_id;
|
line_out->a_id = swap ? b_id : a_id;
|
||||||
line_out->b_id = swap ? a_id : b_id;
|
line_out->b_id = swap ? a_id : b_id;
|
||||||
return true;
|
assert(line_out->a != line_out->b);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a(2) == slice_z) {
|
if (a(2) == slice_z) {
|
||||||
// Only point a alings with the cutting plane.
|
// Only point a alings with the cutting plane.
|
||||||
points_on_layer[num_points_on_layer ++] = num_points;
|
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
|
||||||
IntersectionPoint &point = points[num_points ++];
|
point_on_layer = num_points;
|
||||||
point(0) = a(0);
|
IntersectionPoint &point = points[num_points ++];
|
||||||
point(1) = a(1);
|
point(0) = a(0);
|
||||||
point.point_id = a_id;
|
point(1) = a(1);
|
||||||
|
point.point_id = a_id;
|
||||||
|
}
|
||||||
} else if (b(2) == slice_z) {
|
} else if (b(2) == slice_z) {
|
||||||
// Only point b alings with the cutting plane.
|
// Only point b alings with the cutting plane.
|
||||||
points_on_layer[num_points_on_layer ++] = num_points;
|
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
|
||||||
IntersectionPoint &point = points[num_points ++];
|
point_on_layer = num_points;
|
||||||
point(0) = b(0);
|
IntersectionPoint &point = points[num_points ++];
|
||||||
point(1) = b(1);
|
point(0) = b(0);
|
||||||
point.point_id = b_id;
|
point(1) = b(1);
|
||||||
|
point.point_id = b_id;
|
||||||
|
}
|
||||||
} else if ((a(2) < slice_z && b(2) > slice_z) || (b(2) < slice_z && a(2) > slice_z)) {
|
} else if ((a(2) < slice_z && b(2) > slice_z) || (b(2) < slice_z && a(2) > slice_z)) {
|
||||||
// A general case. The face edge intersects the cutting plane. Calculate the intersection point.
|
// A general case. The face edge intersects the cutting plane. Calculate the intersection point.
|
||||||
IntersectionPoint &point = points[num_points ++];
|
assert(a_id != b_id);
|
||||||
point(0) = b(0) + (a(0) - b(0)) * (slice_z - b(2)) / (a(2) - b(2));
|
// Sort the edge to give a consistent answer.
|
||||||
point(1) = b(1) + (a(1) - b(1)) * (slice_z - b(2)) / (a(2) - b(2));
|
const stl_vertex *pa = &a;
|
||||||
point.edge_id = edge_id;
|
const stl_vertex *pb = &b;
|
||||||
|
if (a_id > b_id) {
|
||||||
|
std::swap(a_id, b_id);
|
||||||
|
std::swap(pa, pb);
|
||||||
|
}
|
||||||
|
IntersectionPoint &point = points[num_points];
|
||||||
|
double t = (double(slice_z) - double((*pb)(2))) / (double((*pa)(2)) - double((*pb)(2)));
|
||||||
|
if (t <= 0.) {
|
||||||
|
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
|
||||||
|
point(0) = (*pa)(0);
|
||||||
|
point(1) = (*pa)(1);
|
||||||
|
point_on_layer = num_points ++;
|
||||||
|
point.point_id = a_id;
|
||||||
|
}
|
||||||
|
} else if (t >= 1.) {
|
||||||
|
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
|
||||||
|
point(0) = (*pb)(0);
|
||||||
|
point(1) = (*pb)(1);
|
||||||
|
point_on_layer = num_points ++;
|
||||||
|
point.point_id = b_id;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
point(0) = coord_t(floor(double((*pb)(0)) + (double((*pa)(0)) - double((*pb)(0))) * t + 0.5));
|
||||||
|
point(1) = coord_t(floor(double((*pb)(1)) + (double((*pa)(1)) - double((*pb)(1))) * t + 0.5));
|
||||||
|
point.edge_id = edge_id;
|
||||||
|
++ num_points;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't have only one point on layer because each vertex gets detected
|
// Facets must intersect each plane 0 or 2 times, or it may touch the plane at a single vertex only.
|
||||||
// twice (once for each edge), and we can't have three points on layer,
|
assert(num_points < 3);
|
||||||
// because we assume this code is not getting called for horizontal facets.
|
|
||||||
assert(num_points_on_layer == 0 || num_points_on_layer == 2);
|
|
||||||
if (num_points_on_layer > 0) {
|
|
||||||
assert(points[points_on_layer[0]].point_id == points[points_on_layer[1]].point_id);
|
|
||||||
assert(num_points == 2 || num_points == 3);
|
|
||||||
if (num_points < 3)
|
|
||||||
// This triangle touches the cutting plane with a single vertex. Ignore it.
|
|
||||||
return false;
|
|
||||||
// Erase one of the duplicate points.
|
|
||||||
-- num_points;
|
|
||||||
for (int i = points_on_layer[1]; i < num_points; ++ i)
|
|
||||||
points[i] = points[i + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Facets must intersect each plane 0 or 2 times.
|
|
||||||
assert(num_points == 0 || num_points == 2);
|
|
||||||
if (num_points == 2) {
|
if (num_points == 2) {
|
||||||
line_out->edge_type = feNone;
|
line_out->edge_type = feGeneral;
|
||||||
line_out->a = (Point)points[1];
|
line_out->a = (Point)points[1];
|
||||||
line_out->b = (Point)points[0];
|
line_out->b = (Point)points[0];
|
||||||
line_out->a_id = points[1].point_id;
|
line_out->a_id = points[1].point_id;
|
||||||
line_out->b_id = points[0].point_id;
|
line_out->b_id = points[0].point_id;
|
||||||
line_out->edge_a_id = points[1].edge_id;
|
line_out->edge_a_id = points[1].edge_id;
|
||||||
line_out->edge_b_id = points[0].edge_id;
|
line_out->edge_b_id = points[0].edge_id;
|
||||||
return true;
|
// Not a zero lenght edge.
|
||||||
|
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
|
||||||
|
//assert(line_out->a != line_out->b);
|
||||||
|
// The plane cuts at least one edge in a general position.
|
||||||
|
assert(line_out->a_id == -1 || line_out->b_id == -1);
|
||||||
|
assert(line_out->edge_a_id != -1 || line_out->edge_b_id != -1);
|
||||||
|
// General slicing position, use the segment for both slicing and object cutting.
|
||||||
|
#if 0
|
||||||
|
if (line_out->a_id != -1 && line_out->b_id != -1) {
|
||||||
|
// Solving a degenerate case, where both the intersections snapped to an edge.
|
||||||
|
// Correctly classify the face as below or above based on the position of the 3rd point.
|
||||||
|
int i = vertices[0];
|
||||||
|
if (i == line_out->a_id || i == line_out->b_id)
|
||||||
|
i = vertices[1];
|
||||||
|
if (i == line_out->a_id || i == line_out->b_id)
|
||||||
|
i = vertices[2];
|
||||||
|
assert(i != line_out->a_id && i != line_out->b_id);
|
||||||
|
line_out->edge_type = (this->v_scaled_shared[i].z < slice_z) ? feTop : feBottom;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return Slicing;
|
||||||
|
}
|
||||||
|
return NoSlice;
|
||||||
|
}
|
||||||
|
|
||||||
|
//FIXME Should this go away? For valid meshes the function slice_facet() returns Slicing
|
||||||
|
// and sets edges of vertical triangles to produce only a single edge per pair of neighbor faces.
|
||||||
|
// So the following code makes only sense now to handle degenerate meshes with more than two faces
|
||||||
|
// sharing a single edge.
|
||||||
|
static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
|
||||||
|
{
|
||||||
|
std::vector<IntersectionLine*> by_vertex_pair;
|
||||||
|
by_vertex_pair.reserve(lines.size());
|
||||||
|
for (IntersectionLine& line : lines)
|
||||||
|
if (line.edge_type != feGeneral && line.a_id != -1)
|
||||||
|
// This is a face edge. Check whether there is its neighbor stored in lines.
|
||||||
|
by_vertex_pair.emplace_back(&line);
|
||||||
|
auto edges_lower_sorted = [](const IntersectionLine *l1, const IntersectionLine *l2) {
|
||||||
|
// Sort vertices of l1, l2 lexicographically
|
||||||
|
int l1a = l1->a_id;
|
||||||
|
int l1b = l1->b_id;
|
||||||
|
int l2a = l2->a_id;
|
||||||
|
int l2b = l2->b_id;
|
||||||
|
if (l1a > l1b)
|
||||||
|
std::swap(l1a, l1b);
|
||||||
|
if (l2a > l2b)
|
||||||
|
std::swap(l2a, l2b);
|
||||||
|
// Lexicographical "lower" operator on lexicographically sorted vertices should bring equal edges together when sored.
|
||||||
|
return l1a < l2a || (l1a == l2a && l1b < l2b);
|
||||||
|
};
|
||||||
|
std::sort(by_vertex_pair.begin(), by_vertex_pair.end(), edges_lower_sorted);
|
||||||
|
for (auto line = by_vertex_pair.begin(); line != by_vertex_pair.end(); ++ line) {
|
||||||
|
IntersectionLine &l1 = **line;
|
||||||
|
if (! l1.skip()) {
|
||||||
|
// Iterate as long as line and line2 edges share the same end points.
|
||||||
|
for (auto line2 = line + 1; line2 != by_vertex_pair.end() && ! edges_lower_sorted(*line, *line2); ++ line2) {
|
||||||
|
// Lines must share the end points.
|
||||||
|
assert(! edges_lower_sorted(*line, *line2));
|
||||||
|
assert(! edges_lower_sorted(*line2, *line));
|
||||||
|
IntersectionLine &l2 = **line2;
|
||||||
|
if (l2.skip())
|
||||||
|
continue;
|
||||||
|
if (l1.a_id == l2.a_id) {
|
||||||
|
assert(l1.b_id == l2.b_id);
|
||||||
|
l2.set_skip();
|
||||||
|
// If they are both oriented upwards or downwards (like a 'V'),
|
||||||
|
// then we can remove both edges from this layer since it won't
|
||||||
|
// affect the sliced shape.
|
||||||
|
// If one of them is oriented upwards and the other is oriented
|
||||||
|
// downwards, let's only keep one of them (it doesn't matter which
|
||||||
|
// one since all 'top' lines were reversed at slicing).
|
||||||
|
if (l1.edge_type == l2.edge_type) {
|
||||||
|
l1.set_skip();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert(l1.a_id == l2.b_id && l1.b_id == l2.a_id);
|
||||||
|
// If this edge joins two horizontal facets, remove both of them.
|
||||||
|
if (l1.edge_type == feHorizontal && l2.edge_type == feHorizontal) {
|
||||||
|
l1.set_skip();
|
||||||
|
l2.set_skip();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
|
void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
|
||||||
{
|
{
|
||||||
// Remove tangent edges.
|
#if 0
|
||||||
//FIXME This is O(n^2) in rare cases when many faces intersect the cutting plane.
|
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
|
||||||
for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++ line)
|
//#ifdef _DEBUG
|
||||||
if (! line->skip && line->edge_type != feNone) {
|
for (const Line &l : lines)
|
||||||
// This line is af facet edge. There may be a duplicate line with the same end vertices.
|
assert(l.a != l.b);
|
||||||
// If the line is is an edge connecting two facets, find another facet edge
|
#endif /* _DEBUG */
|
||||||
// having the same endpoints but in reverse order.
|
|
||||||
for (IntersectionLines::iterator line2 = line + 1; line2 != lines.end(); ++ line2)
|
remove_tangent_edges(lines);
|
||||||
if (! line2->skip && line2->edge_type != feNone) {
|
|
||||||
// Are these facets adjacent? (sharing a common edge on this layer)
|
|
||||||
if (line->a_id == line2->a_id && line->b_id == line2->b_id) {
|
|
||||||
line2->skip = true;
|
|
||||||
/* if they are both oriented upwards or downwards (like a 'V')
|
|
||||||
then we can remove both edges from this layer since it won't
|
|
||||||
affect the sliced shape */
|
|
||||||
/* if one of them is oriented upwards and the other is oriented
|
|
||||||
downwards, let's only keep one of them (it doesn't matter which
|
|
||||||
one since all 'top' lines were reversed at slicing) */
|
|
||||||
if (line->edge_type == line2->edge_type) {
|
|
||||||
line->skip = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (line->a_id == line2->b_id && line->b_id == line2->a_id) {
|
|
||||||
/* if this edge joins two horizontal facets, remove both of them */
|
|
||||||
if (line->edge_type == feHorizontal && line2->edge_type == feHorizontal) {
|
|
||||||
line->skip = true;
|
|
||||||
line2->skip = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OpenPolyline {
|
struct OpenPolyline {
|
||||||
OpenPolyline() {};
|
OpenPolyline() {};
|
||||||
@ -1032,7 +1233,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
by_edge_a_id.reserve(lines.size());
|
by_edge_a_id.reserve(lines.size());
|
||||||
by_a_id.reserve(lines.size());
|
by_a_id.reserve(lines.size());
|
||||||
for (IntersectionLine &line : lines) {
|
for (IntersectionLine &line : lines) {
|
||||||
if (! line.skip) {
|
if (! line.skip()) {
|
||||||
if (line.edge_a_id != -1)
|
if (line.edge_a_id != -1)
|
||||||
by_edge_a_id.emplace_back(&line);
|
by_edge_a_id.emplace_back(&line);
|
||||||
if (line.a_id != -1)
|
if (line.a_id != -1)
|
||||||
@ -1049,13 +1250,14 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
// take first spare line and start a new loop
|
// take first spare line and start a new loop
|
||||||
IntersectionLine *first_line = nullptr;
|
IntersectionLine *first_line = nullptr;
|
||||||
for (; it_line_seed != lines.end(); ++ it_line_seed)
|
for (; it_line_seed != lines.end(); ++ it_line_seed)
|
||||||
if (! it_line_seed->skip) {
|
if (it_line_seed->is_seed_candidate()) {
|
||||||
|
//if (! it_line_seed->skip()) {
|
||||||
first_line = &(*it_line_seed ++);
|
first_line = &(*it_line_seed ++);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (first_line == nullptr)
|
if (first_line == nullptr)
|
||||||
break;
|
break;
|
||||||
first_line->skip = true;
|
first_line->set_skip();
|
||||||
Points loop_pts;
|
Points loop_pts;
|
||||||
loop_pts.emplace_back(first_line->a);
|
loop_pts.emplace_back(first_line->a);
|
||||||
IntersectionLine *last_line = first_line;
|
IntersectionLine *last_line = first_line;
|
||||||
@ -1076,7 +1278,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
if (it_begin != by_edge_a_id.end()) {
|
if (it_begin != by_edge_a_id.end()) {
|
||||||
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
|
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
|
||||||
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
||||||
if (! (*it_line)->skip) {
|
if (! (*it_line)->skip()) {
|
||||||
next_line = *it_line;
|
next_line = *it_line;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1088,7 +1290,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
if (it_begin != by_a_id.end()) {
|
if (it_begin != by_a_id.end()) {
|
||||||
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
|
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
|
||||||
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
|
||||||
if (! (*it_line)->skip) {
|
if (! (*it_line)->skip()) {
|
||||||
next_line = *it_line;
|
next_line = *it_line;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1119,7 +1321,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
*/
|
*/
|
||||||
loop_pts.emplace_back(next_line->a);
|
loop_pts.emplace_back(next_line->a);
|
||||||
last_line = next_line;
|
last_line = next_line;
|
||||||
next_line->skip = true;
|
next_line->set_skip();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1186,12 +1388,12 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (next_start == nullptr) {
|
if (next_start == nullptr) {
|
||||||
// The current loop could not be closed. Unmark the segment.
|
// The current loop could not be closed. Unmark the segment.
|
||||||
opl.consumed = false;
|
opl.consumed = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Attach this polyline to the end of the initial polyline.
|
// Attach this polyline to the end of the initial polyline.
|
||||||
if (next_start->start) {
|
if (next_start->start) {
|
||||||
auto it = next_start->polyline->points.begin();
|
auto it = next_start->polyline->points.begin();
|
||||||
std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points));
|
std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points));
|
||||||
@ -1211,8 +1413,8 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) ||
|
if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) ||
|
||||||
(ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
|
(ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
|
||||||
// The current loop is complete. Add it to the output.
|
// The current loop is complete. Add it to the output.
|
||||||
/*assert(opl.points.front().point_id == opl.points.back().point_id);
|
//assert(opl.points.front().point_id == opl.points.back().point_id);
|
||||||
assert(opl.points.front().edge_id == opl.points.back().edge_id);*/
|
//assert(opl.points.front().edge_id == opl.points.back().edge_id);
|
||||||
// Remove the duplicate last point.
|
// Remove the duplicate last point.
|
||||||
opl.points.pop_back();
|
opl.points.pop_back();
|
||||||
if (opl.points.size() >= 3) {
|
if (opl.points.size() >= 3) {
|
||||||
@ -1227,9 +1429,9 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
|
|||||||
loops->emplace_back(std::move(opl.points));
|
loops->emplace_back(std::move(opl.points));
|
||||||
}
|
}
|
||||||
opl.points.clear();
|
opl.points.clear();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Continue with the current loop.
|
// Continue with the current loop.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1277,15 +1479,15 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
|
|||||||
if (slice_idx == -1)
|
if (slice_idx == -1)
|
||||||
// Ignore this hole.
|
// Ignore this hole.
|
||||||
continue;
|
continue;
|
||||||
assert(current_contour_area < std::numeric_limits<double>::max() && current_contour_area >= -hole->area());
|
assert(current_contour_area < std::numeric_limits<double>::max() && current_contour_area >= -hole->area());
|
||||||
(*slices)[slice_idx].holes.emplace_back(std::move(*hole));
|
(*slices)[slice_idx].holes.emplace_back(std::move(*hole));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// If the input mesh is not valid, the holes may intersect with the external contour.
|
// If the input mesh is not valid, the holes may intersect with the external contour.
|
||||||
// Rather subtract them from the outer contour.
|
// Rather subtract them from the outer contour.
|
||||||
Polygons poly;
|
Polygons poly;
|
||||||
for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) {
|
for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) {
|
||||||
if (it_slice->holes.empty()) {
|
if (it_slice->holes.empty()) {
|
||||||
poly.emplace_back(std::move(it_slice->contour));
|
poly.emplace_back(std::move(it_slice->contour));
|
||||||
} else {
|
} else {
|
||||||
@ -1295,7 +1497,7 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
|
|||||||
it->reverse();
|
it->reverse();
|
||||||
polygons_append(poly, diff(contours, it_slice->holes));
|
polygons_append(poly, diff(contours, it_slice->holes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the input mesh is not valid, the input contours may intersect.
|
// If the input mesh is not valid, the input contours may intersect.
|
||||||
*slices = union_ex(poly);
|
*slices = union_ex(poly);
|
||||||
#endif
|
#endif
|
||||||
@ -1412,7 +1614,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
|
|||||||
|
|
||||||
// intersect facet with cutting plane
|
// intersect facet with cutting plane
|
||||||
IntersectionLine line;
|
IntersectionLine line;
|
||||||
if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line)) {
|
if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line) != TriangleMeshSlicer::NoSlice) {
|
||||||
// Save intersection lines for generating correct triangulations.
|
// Save intersection lines for generating correct triangulations.
|
||||||
if (line.edge_type == feTop) {
|
if (line.edge_type == feTop) {
|
||||||
lower_lines.emplace_back(line);
|
lower_lines.emplace_back(line);
|
||||||
|
@ -83,7 +83,7 @@ private:
|
|||||||
|
|
||||||
enum FacetEdgeType {
|
enum FacetEdgeType {
|
||||||
// A general case, the cutting plane intersect a face at two different edges.
|
// A general case, the cutting plane intersect a face at two different edges.
|
||||||
feNone,
|
feGeneral,
|
||||||
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
|
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
|
||||||
feTop,
|
feTop,
|
||||||
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
|
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
|
||||||
@ -117,6 +117,14 @@ public:
|
|||||||
class IntersectionLine : public Line
|
class IntersectionLine : public Line
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feGeneral), flags(0) {}
|
||||||
|
|
||||||
|
bool skip() const { return (this->flags & SKIP) != 0; }
|
||||||
|
void set_skip() { this->flags |= SKIP; }
|
||||||
|
|
||||||
|
bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
|
||||||
|
void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
|
||||||
|
|
||||||
// Inherits Point a, b
|
// Inherits Point a, b
|
||||||
// For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
|
// For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
|
||||||
// Vertex indices of the line end points.
|
// Vertex indices of the line end points.
|
||||||
@ -125,11 +133,23 @@ public:
|
|||||||
// Source mesh edges of the line end points.
|
// Source mesh edges of the line end points.
|
||||||
int edge_a_id;
|
int edge_a_id;
|
||||||
int edge_b_id;
|
int edge_b_id;
|
||||||
// feNone, feTop, feBottom, feHorizontal
|
// feGeneral, feTop, feBottom, feHorizontal
|
||||||
FacetEdgeType edge_type;
|
FacetEdgeType edge_type;
|
||||||
// Used by TriangleMeshSlicer::make_loops() to skip duplicate edges.
|
// Used by TriangleMeshSlicer::slice() to skip duplicate edges.
|
||||||
bool skip;
|
enum {
|
||||||
IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feNone), skip(false) {};
|
// Triangle edge added, because it has no neighbor.
|
||||||
|
EDGE0_NO_NEIGHBOR = 0x001,
|
||||||
|
EDGE1_NO_NEIGHBOR = 0x002,
|
||||||
|
EDGE2_NO_NEIGHBOR = 0x004,
|
||||||
|
// Triangle edge added, because it makes a fold with another horizontal edge.
|
||||||
|
EDGE0_FOLD = 0x010,
|
||||||
|
EDGE1_FOLD = 0x020,
|
||||||
|
EDGE2_FOLD = 0x040,
|
||||||
|
// The edge cannot be a seed of a greedy loop extraction (folds are not safe to become seeds).
|
||||||
|
NO_SEED = 0x100,
|
||||||
|
SKIP = 0x200,
|
||||||
|
};
|
||||||
|
uint32_t flags;
|
||||||
};
|
};
|
||||||
typedef std::vector<IntersectionLine> IntersectionLines;
|
typedef std::vector<IntersectionLine> IntersectionLines;
|
||||||
typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
|
typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
|
||||||
@ -144,7 +164,12 @@ public:
|
|||||||
void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
|
void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
|
||||||
void slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
|
void slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
|
||||||
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
|
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
|
||||||
bool slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
|
enum FacetSliceType {
|
||||||
|
NoSlice = 0,
|
||||||
|
Slicing = 1,
|
||||||
|
Cutting = 2
|
||||||
|
};
|
||||||
|
FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
|
||||||
const float min_z, const float max_z, IntersectionLine *line_out) const;
|
const float min_z, const float max_z, IntersectionLine *line_out) const;
|
||||||
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
|
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ namespace Slic3r {
|
|||||||
|
|
||||||
extern void set_logging_level(unsigned int level);
|
extern void set_logging_level(unsigned int level);
|
||||||
extern void trace(unsigned int level, const char *message);
|
extern void trace(unsigned int level, const char *message);
|
||||||
|
extern void disable_multi_threading();
|
||||||
|
|
||||||
// Set a path with GUI resource files.
|
// Set a path with GUI resource files.
|
||||||
void set_var_dir(const std::string &path);
|
void set_var_dir(const std::string &path);
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
|
#include "Technologies.hpp"
|
||||||
|
|
||||||
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
|
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
|
||||||
#define SLIC3R_VERSION "1.41.0"
|
#define SLIC3R_VERSION "1.41.0"
|
||||||
#define SLIC3R_BUILD "UNKNOWN"
|
#define SLIC3R_BUILD "UNKNOWN"
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#include <boost/nowide/convert.hpp>
|
#include <boost/nowide/convert.hpp>
|
||||||
#include <boost/nowide/cstdio.hpp>
|
#include <boost/nowide/cstdio.hpp>
|
||||||
|
|
||||||
|
#include <tbb/task_scheduler_init.h>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error;
|
static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error;
|
||||||
@ -83,6 +85,14 @@ void trace(unsigned int level, const char *message)
|
|||||||
(::boost::log::keywords::severity = severity)) << message;
|
(::boost::log::keywords::severity = severity)) << message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void disable_multi_threading()
|
||||||
|
{
|
||||||
|
// Disable parallelization so the Shiny profiler works
|
||||||
|
static tbb::task_scheduler_init *tbb_init = nullptr;
|
||||||
|
if (tbb_init == nullptr)
|
||||||
|
tbb_init = new tbb::task_scheduler_init(1);
|
||||||
|
}
|
||||||
|
|
||||||
static std::string g_var_dir;
|
static std::string g_var_dir;
|
||||||
|
|
||||||
void set_var_dir(const std::string &dir)
|
void set_var_dir(const std::string &dir)
|
||||||
|
@ -11,12 +11,11 @@
|
|||||||
#include <ModelArrange.hpp>
|
#include <ModelArrange.hpp>
|
||||||
#include <slic3r/GUI/PresetBundle.hpp>
|
#include <slic3r/GUI/PresetBundle.hpp>
|
||||||
|
|
||||||
#include <Print.hpp>
|
|
||||||
#include <PrintExport.hpp>
|
|
||||||
#include <Geometry.hpp>
|
#include <Geometry.hpp>
|
||||||
|
#include <PrintConfig.hpp>
|
||||||
|
#include <Print.hpp>
|
||||||
#include <Model.hpp>
|
#include <Model.hpp>
|
||||||
#include <Utils.hpp>
|
#include <Utils.hpp>
|
||||||
#include <SLABasePool.hpp>
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -44,15 +43,6 @@ namespace GUI {
|
|||||||
PresetBundle* get_preset_bundle();
|
PresetBundle* get_preset_bundle();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const PrintObjectStep STEP_SLICE = posSlice;
|
|
||||||
static const PrintObjectStep STEP_PERIMETERS = posPerimeters;
|
|
||||||
static const PrintObjectStep STEP_PREPARE_INFILL = posPrepareInfill;
|
|
||||||
static const PrintObjectStep STEP_INFILL = posInfill;
|
|
||||||
static const PrintObjectStep STEP_SUPPORTMATERIAL = posSupportMaterial;
|
|
||||||
static const PrintStep STEP_SKIRT = psSkirt;
|
|
||||||
static const PrintStep STEP_BRIM = psBrim;
|
|
||||||
static const PrintStep STEP_WIPE_TOWER = psWipeTower;
|
|
||||||
|
|
||||||
AppControllerBoilerplate::ProgresIndicatorPtr
|
AppControllerBoilerplate::ProgresIndicatorPtr
|
||||||
AppControllerBoilerplate::global_progress_indicator() {
|
AppControllerBoilerplate::global_progress_indicator() {
|
||||||
ProgresIndicatorPtr ret;
|
ProgresIndicatorPtr ret;
|
||||||
@ -72,343 +62,8 @@ void AppControllerBoilerplate::global_progress_indicator(
|
|||||||
pri_data_->m.unlock();
|
pri_data_->m.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
void PrintController::make_skirt()
|
|
||||||
{
|
|
||||||
assert(print_ != nullptr);
|
|
||||||
|
|
||||||
// prerequisites
|
|
||||||
for(auto obj : print_->objects()) make_perimeters(obj);
|
|
||||||
for(auto obj : print_->objects()) infill(obj);
|
|
||||||
for(auto obj : print_->objects()) gen_support_material(obj);
|
|
||||||
|
|
||||||
if(!print_->state.is_done(STEP_SKIRT)) {
|
|
||||||
print_->state.set_started(STEP_SKIRT);
|
|
||||||
print_->skirt.clear();
|
|
||||||
if(print_->has_skirt()) print_->_make_skirt();
|
|
||||||
|
|
||||||
print_->state.set_done(STEP_SKIRT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintController::make_brim()
|
|
||||||
{
|
|
||||||
assert(print_ != nullptr);
|
|
||||||
|
|
||||||
// prerequisites
|
|
||||||
for(auto obj : print_->objects()) make_perimeters(obj);
|
|
||||||
for(auto obj : print_->objects()) infill(obj);
|
|
||||||
for(auto obj : print_->objects()) gen_support_material(obj);
|
|
||||||
make_skirt();
|
|
||||||
|
|
||||||
if(!print_->state.is_done(STEP_BRIM)) {
|
|
||||||
print_->state.set_started(STEP_BRIM);
|
|
||||||
|
|
||||||
// since this method must be idempotent, we clear brim paths *before*
|
|
||||||
// checking whether we need to generate them
|
|
||||||
print_->brim.clear();
|
|
||||||
|
|
||||||
if(print_->config.brim_width > 0) print_->_make_brim();
|
|
||||||
|
|
||||||
print_->state.set_done(STEP_BRIM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintController::make_wipe_tower()
|
|
||||||
{
|
|
||||||
assert(print_ != nullptr);
|
|
||||||
|
|
||||||
// prerequisites
|
|
||||||
for(auto obj : print_->objects()) make_perimeters(obj);
|
|
||||||
for(auto obj : print_->objects()) infill(obj);
|
|
||||||
for(auto obj : print_->objects()) gen_support_material(obj);
|
|
||||||
make_skirt();
|
|
||||||
make_brim();
|
|
||||||
|
|
||||||
if(!print_->state.is_done(STEP_WIPE_TOWER)) {
|
|
||||||
print_->state.set_started(STEP_WIPE_TOWER);
|
|
||||||
|
|
||||||
// since this method must be idempotent, we clear brim paths *before*
|
|
||||||
// checking whether we need to generate them
|
|
||||||
print_->brim.clear();
|
|
||||||
|
|
||||||
if(print_->has_wipe_tower()) print_->_make_wipe_tower();
|
|
||||||
|
|
||||||
print_->state.set_done(STEP_WIPE_TOWER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintController::slice(PrintObject *pobj)
|
|
||||||
{
|
|
||||||
assert(pobj != nullptr && print_ != nullptr);
|
|
||||||
|
|
||||||
if(pobj->state.is_done(STEP_SLICE)) return;
|
|
||||||
|
|
||||||
pobj->state.set_started(STEP_SLICE);
|
|
||||||
|
|
||||||
pobj->_slice();
|
|
||||||
|
|
||||||
auto msg = pobj->_fix_slicing_errors();
|
|
||||||
if(!msg.empty()) report_issue(IssueType::WARN, msg);
|
|
||||||
|
|
||||||
// simplify slices if required
|
|
||||||
if (print_->config.resolution)
|
|
||||||
pobj->_simplify_slices(scale_(print_->config.resolution));
|
|
||||||
|
|
||||||
|
|
||||||
if(pobj->layers.empty())
|
|
||||||
report_issue(IssueType::ERR,
|
|
||||||
_(L("No layers were detected. You might want to repair your "
|
|
||||||
"STL file(s) or check their size or thickness and retry"))
|
|
||||||
);
|
|
||||||
|
|
||||||
pobj->state.set_done(STEP_SLICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintController::make_perimeters(PrintObject *pobj)
|
|
||||||
{
|
|
||||||
assert(pobj != nullptr);
|
|
||||||
|
|
||||||
slice(pobj);
|
|
||||||
|
|
||||||
if (!pobj->state.is_done(STEP_PERIMETERS)) {
|
|
||||||
pobj->_make_perimeters();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintController::infill(PrintObject *pobj)
|
|
||||||
{
|
|
||||||
assert(pobj != nullptr);
|
|
||||||
|
|
||||||
make_perimeters(pobj);
|
|
||||||
|
|
||||||
if (!pobj->state.is_done(STEP_PREPARE_INFILL)) {
|
|
||||||
pobj->state.set_started(STEP_PREPARE_INFILL);
|
|
||||||
|
|
||||||
pobj->_prepare_infill();
|
|
||||||
|
|
||||||
pobj->state.set_done(STEP_PREPARE_INFILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
pobj->_infill();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintController::gen_support_material(PrintObject *pobj)
|
|
||||||
{
|
|
||||||
assert(pobj != nullptr);
|
|
||||||
|
|
||||||
// prerequisites
|
|
||||||
slice(pobj);
|
|
||||||
|
|
||||||
if(!pobj->state.is_done(STEP_SUPPORTMATERIAL)) {
|
|
||||||
pobj->state.set_started(STEP_SUPPORTMATERIAL);
|
|
||||||
|
|
||||||
pobj->clear_support_layers();
|
|
||||||
|
|
||||||
if((pobj->config.support_material || pobj->config.raft_layers > 0)
|
|
||||||
&& pobj->layers.size() > 1) {
|
|
||||||
pobj->_generate_support_material();
|
|
||||||
}
|
|
||||||
|
|
||||||
pobj->state.set_done(STEP_SUPPORTMATERIAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintController::PngExportData
|
|
||||||
PrintController::query_png_export_data(const DynamicPrintConfig& conf)
|
|
||||||
{
|
|
||||||
PngExportData ret;
|
|
||||||
|
|
||||||
auto zippath = query_destination_path("Output zip file", "*.zip", "out");
|
|
||||||
|
|
||||||
ret.zippath = zippath;
|
|
||||||
|
|
||||||
ret.width_mm = conf.opt_float("display_width");
|
|
||||||
ret.height_mm = conf.opt_float("display_height");
|
|
||||||
|
|
||||||
ret.width_px = conf.opt_int("display_pixels_x");
|
|
||||||
ret.height_px = conf.opt_int("display_pixels_y");
|
|
||||||
|
|
||||||
auto opt_corr = conf.opt<ConfigOptionFloats>("printer_correction");
|
|
||||||
|
|
||||||
if(opt_corr) {
|
|
||||||
ret.corr_x = opt_corr->values[0];
|
|
||||||
ret.corr_y = opt_corr->values[1];
|
|
||||||
ret.corr_z = opt_corr->values[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.exp_time_first_s = conf.opt_float("initial_exposure_time");
|
|
||||||
ret.exp_time_s = conf.opt_float("exposure_time");
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri)
|
|
||||||
{
|
|
||||||
auto st = pri->state();
|
|
||||||
|
|
||||||
Slic3r::trace(3, "Starting the slicing process.");
|
|
||||||
|
|
||||||
pri->update(st+20, _(L("Generating perimeters")));
|
|
||||||
for(auto obj : print_->objects()) make_perimeters(obj);
|
|
||||||
|
|
||||||
pri->update(st+60, _(L("Infilling layers")));
|
|
||||||
for(auto obj : print_->objects()) infill(obj);
|
|
||||||
|
|
||||||
pri->update(st+70, _(L("Generating support material")));
|
|
||||||
for(auto obj : print_->objects()) gen_support_material(obj);
|
|
||||||
|
|
||||||
pri->message_fmt(_(L("Weight: %.1fg, Cost: %.1f")),
|
|
||||||
print_->total_weight, print_->total_cost);
|
|
||||||
pri->state(st+85);
|
|
||||||
|
|
||||||
|
|
||||||
pri->update(st+88, _(L("Generating skirt")));
|
|
||||||
make_skirt();
|
|
||||||
|
|
||||||
|
|
||||||
pri->update(st+90, _(L("Generating brim")));
|
|
||||||
make_brim();
|
|
||||||
|
|
||||||
pri->update(st+95, _(L("Generating wipe tower")));
|
|
||||||
make_wipe_tower();
|
|
||||||
|
|
||||||
pri->update(st+100, _(L("Done")));
|
|
||||||
|
|
||||||
// time to make some statistics..
|
|
||||||
|
|
||||||
Slic3r::trace(3, _(L("Slicing process finished.")));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintController::slice()
|
|
||||||
{
|
|
||||||
auto pri = global_progress_indicator();
|
|
||||||
if(!pri) pri = create_progress_indicator(100, L("Slicing"));
|
|
||||||
slice(pri);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrintController::slice_to_png()
|
|
||||||
{
|
|
||||||
using Pointf3 = Vec3d;
|
|
||||||
|
|
||||||
auto presetbundle = GUI::get_preset_bundle();
|
|
||||||
|
|
||||||
assert(presetbundle);
|
|
||||||
|
|
||||||
auto pt = presetbundle->printers.get_selected_preset().printer_technology();
|
|
||||||
if(pt != ptSLA) {
|
|
||||||
report_issue(IssueType::ERR, _("Printer technology is not SLA!"),
|
|
||||||
_("Error"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto conf = presetbundle->full_config();
|
|
||||||
conf.validate();
|
|
||||||
|
|
||||||
auto exd = query_png_export_data(conf);
|
|
||||||
if(exd.zippath.empty()) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
print_->apply_config(conf);
|
|
||||||
print_->validate();
|
|
||||||
} catch(std::exception& e) {
|
|
||||||
report_issue(IssueType::ERR, e.what(), "Error");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: copy the model and work with the copy only
|
|
||||||
bool correction = false;
|
|
||||||
if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) {
|
|
||||||
correction = true;
|
|
||||||
print_->invalidate_all_steps();
|
|
||||||
|
|
||||||
for(auto po : print_->objects()) {
|
|
||||||
po->model_object()->scale(
|
|
||||||
Pointf3(exd.corr_x, exd.corr_y, exd.corr_z)
|
|
||||||
);
|
|
||||||
po->model_object()->invalidate_bounding_box();
|
|
||||||
po->reload_model_instances();
|
|
||||||
po->invalidate_all_steps();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn back the correction scaling on the model.
|
|
||||||
auto scale_back = [this, correction, exd]() {
|
|
||||||
if(correction) { // scale the model back
|
|
||||||
print_->invalidate_all_steps();
|
|
||||||
for(auto po : print_->objects()) {
|
|
||||||
po->model_object()->scale(
|
|
||||||
Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z)
|
|
||||||
);
|
|
||||||
po->model_object()->invalidate_bounding_box();
|
|
||||||
po->reload_model_instances();
|
|
||||||
po->invalidate_all_steps();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto print_bb = print_->bounding_box();
|
|
||||||
Vec2d punsc = unscale(print_bb.size());
|
|
||||||
|
|
||||||
// If the print does not fit into the print area we should cry about it.
|
|
||||||
if(px(punsc) > exd.width_mm || py(punsc) > exd.height_mm) {
|
|
||||||
std::stringstream ss;
|
|
||||||
|
|
||||||
ss << _(L("Print will not fit and will be truncated!")) << "\n"
|
|
||||||
<< _(L("Width needed: ")) << px(punsc) << " mm\n"
|
|
||||||
<< _(L("Height needed: ")) << py(punsc) << " mm\n";
|
|
||||||
|
|
||||||
if(!report_issue(IssueType::WARN_Q, ss.str(), _(L("Warning")))) {
|
|
||||||
scale_back();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// std::async(supports_asynch()? std::launch::async : std::launch::deferred,
|
|
||||||
// [this, exd, scale_back]()
|
|
||||||
// {
|
|
||||||
|
|
||||||
auto pri = create_progress_indicator(
|
|
||||||
200, _(L("Slicing to zipped png files...")));
|
|
||||||
|
|
||||||
try {
|
|
||||||
pri->update(0, _(L("Slicing...")));
|
|
||||||
slice(pri);
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
pri->cancel();
|
|
||||||
report_issue(IssueType::ERR, e.what(), _(L("Exception occured")));
|
|
||||||
scale_back();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto pbak = print_->progressindicator;
|
|
||||||
print_->progressindicator = pri;
|
|
||||||
|
|
||||||
try {
|
|
||||||
print_to<FilePrinterFormat::PNG>( *print_, exd.zippath,
|
|
||||||
exd.width_mm, exd.height_mm,
|
|
||||||
exd.width_px, exd.height_px,
|
|
||||||
exd.exp_time_s, exd.exp_time_first_s);
|
|
||||||
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
pri->cancel();
|
|
||||||
report_issue(IssueType::ERR, e.what(), _(L("Exception occured")));
|
|
||||||
}
|
|
||||||
|
|
||||||
print_->progressindicator = pbak;
|
|
||||||
scale_back();
|
|
||||||
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
const PrintConfig &PrintController::config() const
|
|
||||||
{
|
|
||||||
return print_->config;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void ProgressIndicator::message_fmt(
|
void ProgressIndicator::message_fmt(
|
||||||
const wxString &fmtstr, ...) {
|
const std::string &fmtstr, ...) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmtstr);
|
va_start(args, fmtstr);
|
||||||
@ -438,6 +93,11 @@ void AppController::arrange_model()
|
|||||||
{
|
{
|
||||||
using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
|
using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
|
||||||
|
|
||||||
|
if(arranging_.load()) return;
|
||||||
|
|
||||||
|
// to prevent UI reentrancies
|
||||||
|
arranging_.store(true);
|
||||||
|
|
||||||
unsigned count = 0;
|
unsigned count = 0;
|
||||||
for(auto obj : model_->objects) count += obj->instances.size();
|
for(auto obj : model_->objects) count += obj->instances.size();
|
||||||
|
|
||||||
@ -451,8 +111,8 @@ void AppController::arrange_model()
|
|||||||
// Set the range of the progress to the object count
|
// Set the range of the progress to the object count
|
||||||
pind->max(count);
|
pind->max(count);
|
||||||
|
|
||||||
pind->on_cancel([](){
|
pind->on_cancel([this](){
|
||||||
std::cout << "Cannot be cancelled!" << std::endl;
|
arranging_.store(false);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,7 +126,7 @@ void AppController::arrange_model()
|
|||||||
for(auto& v : bedpoints)
|
for(auto& v : bedpoints)
|
||||||
bed.append(Point::new_scale(v(0), v(1)));
|
bed.append(Point::new_scale(v(0), v(1)));
|
||||||
|
|
||||||
if(pind) pind->update(0, _(L("Arranging objects...")));
|
if(pind) pind->update(0, L("Arranging objects..."));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
arr::BedShapeHint hint;
|
arr::BedShapeHint hint;
|
||||||
@ -478,24 +138,30 @@ void AppController::arrange_model()
|
|||||||
bed,
|
bed,
|
||||||
hint,
|
hint,
|
||||||
false, // create many piles not just one pile
|
false, // create many piles not just one pile
|
||||||
[pind, count](unsigned rem) {
|
[this, pind, count](unsigned rem) {
|
||||||
if(pind)
|
if(pind)
|
||||||
pind->update(count - rem, _(L("Arranging objects...")));
|
pind->update(count - rem, L("Arranging objects..."));
|
||||||
});
|
|
||||||
|
process_events();
|
||||||
|
}, [this] () { return !arranging_.load(); });
|
||||||
} catch(std::exception& e) {
|
} catch(std::exception& e) {
|
||||||
std::cerr << e.what() << std::endl;
|
std::cerr << e.what() << std::endl;
|
||||||
report_issue(IssueType::ERR,
|
report_issue(IssueType::ERR,
|
||||||
_(L("Could not arrange model objects! "
|
L("Could not arrange model objects! "
|
||||||
"Some geometries may be invalid.")),
|
"Some geometries may be invalid."),
|
||||||
_(L("Exception occurred")));
|
L("Exception occurred"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore previous max value
|
// Restore previous max value
|
||||||
if(pind) {
|
if(pind) {
|
||||||
pind->max(pmax);
|
pind->max(pmax);
|
||||||
pind->update(0, _(L("Arranging done.")));
|
pind->update(0, arranging_.load() ? L("Arranging done.") :
|
||||||
|
L("Arranging canceled."));
|
||||||
|
|
||||||
pind->on_cancel(/*remove cancel function*/);
|
pind->on_cancel(/*remove cancel function*/);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arranging_.store(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ class Print;
|
|||||||
class PrintObject;
|
class PrintObject;
|
||||||
class PrintConfig;
|
class PrintConfig;
|
||||||
class ProgressStatusBar;
|
class ProgressStatusBar;
|
||||||
|
class DynamicPrintConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A boilerplate class for creating application logic. It should provide
|
* @brief A boilerplate class for creating application logic. It should provide
|
||||||
@ -48,7 +49,7 @@ public:
|
|||||||
AppControllerBoilerplate();
|
AppControllerBoilerplate();
|
||||||
~AppControllerBoilerplate();
|
~AppControllerBoilerplate();
|
||||||
|
|
||||||
using Path = wxString;
|
using Path = std::string;
|
||||||
using PathList = std::vector<Path>;
|
using PathList = std::vector<Path>;
|
||||||
|
|
||||||
/// Common runtime issue types
|
/// Common runtime issue types
|
||||||
@ -69,20 +70,20 @@ public:
|
|||||||
* @return Returns a list of paths choosed by the user.
|
* @return Returns a list of paths choosed by the user.
|
||||||
*/
|
*/
|
||||||
PathList query_destination_paths(
|
PathList query_destination_paths(
|
||||||
const wxString& title,
|
const std::string& title,
|
||||||
const std::string& extensions) const;
|
const std::string& extensions) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Same as query_destination_paths but works for directories only.
|
* @brief Same as query_destination_paths but works for directories only.
|
||||||
*/
|
*/
|
||||||
PathList query_destination_dirs(
|
PathList query_destination_dirs(
|
||||||
const wxString& title) const;
|
const std::string& title) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Same as query_destination_paths but returns only one path.
|
* @brief Same as query_destination_paths but returns only one path.
|
||||||
*/
|
*/
|
||||||
Path query_destination_path(
|
Path query_destination_path(
|
||||||
const wxString& title,
|
const std::string& title,
|
||||||
const std::string& extensions,
|
const std::string& extensions,
|
||||||
const std::string& hint = "") const;
|
const std::string& hint = "") const;
|
||||||
|
|
||||||
@ -97,11 +98,11 @@ public:
|
|||||||
* title.
|
* title.
|
||||||
*/
|
*/
|
||||||
bool report_issue(IssueType issuetype,
|
bool report_issue(IssueType issuetype,
|
||||||
const wxString& description,
|
const std::string& description,
|
||||||
const wxString& brief);
|
const std::string& brief);
|
||||||
|
|
||||||
bool report_issue(IssueType issuetype,
|
bool report_issue(IssueType issuetype,
|
||||||
const wxString& description);
|
const std::string& description);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Return the global progress indicator for the current controller.
|
* @brief Return the global progress indicator for the current controller.
|
||||||
@ -152,12 +153,12 @@ protected:
|
|||||||
*/
|
*/
|
||||||
ProgresIndicatorPtr create_progress_indicator(
|
ProgresIndicatorPtr create_progress_indicator(
|
||||||
unsigned statenum,
|
unsigned statenum,
|
||||||
const wxString& title,
|
const std::string& title,
|
||||||
const wxString& firstmsg) const;
|
const std::string& firstmsg) const;
|
||||||
|
|
||||||
ProgresIndicatorPtr create_progress_indicator(
|
ProgresIndicatorPtr create_progress_indicator(
|
||||||
unsigned statenum,
|
unsigned statenum,
|
||||||
const wxString& title) const;
|
const std::string& title) const;
|
||||||
|
|
||||||
// This is a global progress indicator placeholder. In the Slic3r UI it can
|
// This is a global progress indicator placeholder. In the Slic3r UI it can
|
||||||
// contain the progress indicator on the statusbar.
|
// contain the progress indicator on the statusbar.
|
||||||
@ -170,43 +171,6 @@ protected:
|
|||||||
*/
|
*/
|
||||||
class PrintController: public AppControllerBoilerplate {
|
class PrintController: public AppControllerBoilerplate {
|
||||||
Print *print_ = nullptr;
|
Print *print_ = nullptr;
|
||||||
protected:
|
|
||||||
|
|
||||||
void make_skirt();
|
|
||||||
void make_brim();
|
|
||||||
void make_wipe_tower();
|
|
||||||
|
|
||||||
void make_perimeters(PrintObject *pobj);
|
|
||||||
void infill(PrintObject *pobj);
|
|
||||||
void gen_support_material(PrintObject *pobj);
|
|
||||||
|
|
||||||
// Data structure with the png export input data
|
|
||||||
struct PngExportData {
|
|
||||||
std::string zippath; // output zip file
|
|
||||||
unsigned long width_px = 1440; // resolution - rows
|
|
||||||
unsigned long height_px = 2560; // resolution columns
|
|
||||||
double width_mm = 68.0, height_mm = 120.0; // dimensions in mm
|
|
||||||
double exp_time_first_s = 35.0; // first exposure time
|
|
||||||
double exp_time_s = 8.0; // global exposure time
|
|
||||||
double corr_x = 1.0; // offsetting in x
|
|
||||||
double corr_y = 1.0; // offsetting in y
|
|
||||||
double corr_z = 1.0; // offsetting in y
|
|
||||||
};
|
|
||||||
|
|
||||||
// Should display a dialog with the input fields for printing to png
|
|
||||||
PngExportData query_png_export_data(const DynamicPrintConfig&);
|
|
||||||
|
|
||||||
// The previous export data, to pre-populate the dialog
|
|
||||||
PngExportData prev_expdata_;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Slice one pront object.
|
|
||||||
* @param pobj The print object.
|
|
||||||
*/
|
|
||||||
void slice(PrintObject *pobj);
|
|
||||||
|
|
||||||
void slice(ProgresIndicatorPtr pri);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Must be public for perl to use it
|
// Must be public for perl to use it
|
||||||
@ -221,15 +185,8 @@ public:
|
|||||||
return PrintController::Ptr( new PrintController(print) );
|
return PrintController::Ptr( new PrintController(print) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void slice() {}
|
||||||
* @brief Slice the loaded print scene.
|
void slice_to_png() {}
|
||||||
*/
|
|
||||||
void slice();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Slice the print into zipped png files.
|
|
||||||
*/
|
|
||||||
void slice_to_png();
|
|
||||||
|
|
||||||
const PrintConfig& config() const;
|
const PrintConfig& config() const;
|
||||||
};
|
};
|
||||||
@ -253,6 +210,7 @@ public:
|
|||||||
class AppController: public AppControllerBoilerplate {
|
class AppController: public AppControllerBoilerplate {
|
||||||
Model *model_ = nullptr;
|
Model *model_ = nullptr;
|
||||||
PrintController::Ptr printctl;
|
PrintController::Ptr printctl;
|
||||||
|
std::atomic<bool> arranging_;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -288,7 +246,7 @@ public:
|
|||||||
* In perl we have a progress indicating status bar on the bottom of the
|
* In perl we have a progress indicating status bar on the bottom of the
|
||||||
* window which is defined and created in perl. We can pass the ID-s of the
|
* window which is defined and created in perl. We can pass the ID-s of the
|
||||||
* gauge and the statusbar id and make a wrapper implementation of the
|
* gauge and the statusbar id and make a wrapper implementation of the
|
||||||
* IProgressIndicator interface so we can use this GUI widget from C++.
|
* ProgressIndicator interface so we can use this GUI widget from C++.
|
||||||
*
|
*
|
||||||
* This function should be called from perl.
|
* This function should be called from perl.
|
||||||
*
|
*
|
||||||
|
@ -28,16 +28,16 @@ bool AppControllerBoilerplate::supports_asynch() const
|
|||||||
|
|
||||||
void AppControllerBoilerplate::process_events()
|
void AppControllerBoilerplate::process_events()
|
||||||
{
|
{
|
||||||
wxSafeYield();
|
wxYieldIfNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
AppControllerBoilerplate::PathList
|
AppControllerBoilerplate::PathList
|
||||||
AppControllerBoilerplate::query_destination_paths(
|
AppControllerBoilerplate::query_destination_paths(
|
||||||
const wxString &title,
|
const std::string &title,
|
||||||
const std::string &extensions) const
|
const std::string &extensions) const
|
||||||
{
|
{
|
||||||
|
|
||||||
wxFileDialog dlg(wxTheApp->GetTopWindow(), title );
|
wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
|
||||||
dlg.SetWildcard(extensions);
|
dlg.SetWildcard(extensions);
|
||||||
|
|
||||||
dlg.ShowModal();
|
dlg.ShowModal();
|
||||||
@ -53,11 +53,11 @@ AppControllerBoilerplate::query_destination_paths(
|
|||||||
|
|
||||||
AppControllerBoilerplate::Path
|
AppControllerBoilerplate::Path
|
||||||
AppControllerBoilerplate::query_destination_path(
|
AppControllerBoilerplate::query_destination_path(
|
||||||
const wxString &title,
|
const std::string &title,
|
||||||
const std::string &extensions,
|
const std::string &extensions,
|
||||||
const std::string& hint) const
|
const std::string& hint) const
|
||||||
{
|
{
|
||||||
wxFileDialog dlg(wxTheApp->GetTopWindow(), title );
|
wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
|
||||||
dlg.SetWildcard(extensions);
|
dlg.SetWildcard(extensions);
|
||||||
|
|
||||||
dlg.SetFilename(hint);
|
dlg.SetFilename(hint);
|
||||||
@ -72,8 +72,8 @@ AppControllerBoilerplate::query_destination_path(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool AppControllerBoilerplate::report_issue(IssueType issuetype,
|
bool AppControllerBoilerplate::report_issue(IssueType issuetype,
|
||||||
const wxString &description,
|
const std::string &description,
|
||||||
const wxString &brief)
|
const std::string &brief)
|
||||||
{
|
{
|
||||||
auto icon = wxICON_INFORMATION;
|
auto icon = wxICON_INFORMATION;
|
||||||
auto style = wxOK|wxCENTRE;
|
auto style = wxOK|wxCENTRE;
|
||||||
@ -85,15 +85,15 @@ bool AppControllerBoilerplate::report_issue(IssueType issuetype,
|
|||||||
case IssueType::FATAL: icon = wxICON_ERROR;
|
case IssueType::FATAL: icon = wxICON_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ret = wxMessageBox(description, brief, icon | style);
|
auto ret = wxMessageBox(_(description), _(brief), icon | style);
|
||||||
return ret != wxCANCEL;
|
return ret != wxCANCEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AppControllerBoilerplate::report_issue(
|
bool AppControllerBoilerplate::report_issue(
|
||||||
AppControllerBoilerplate::IssueType issuetype,
|
AppControllerBoilerplate::IssueType issuetype,
|
||||||
const wxString &description)
|
const std::string &description)
|
||||||
{
|
{
|
||||||
return report_issue(issuetype, description, wxString());
|
return report_issue(issuetype, description, std::string());
|
||||||
}
|
}
|
||||||
|
|
||||||
wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
|
wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
|
||||||
@ -126,7 +126,9 @@ class GuiProgressIndicator:
|
|||||||
void _state( unsigned st) {
|
void _state( unsigned st) {
|
||||||
if(!gauge_.IsShown()) gauge_.ShowModal();
|
if(!gauge_.IsShown()) gauge_.ShowModal();
|
||||||
Base::state(st);
|
Base::state(st);
|
||||||
gauge_.Update(static_cast<int>(st), message_);
|
if(!gauge_.Update(static_cast<int>(st), message_)) {
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -140,7 +142,8 @@ public:
|
|||||||
inline GuiProgressIndicator(int range, const wxString& title,
|
inline GuiProgressIndicator(int range, const wxString& title,
|
||||||
const wxString& firstmsg) :
|
const wxString& firstmsg) :
|
||||||
gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(),
|
gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(),
|
||||||
wxPD_APP_MODAL | wxPD_AUTO_HIDE),
|
wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT),
|
||||||
|
|
||||||
message_(firstmsg),
|
message_(firstmsg),
|
||||||
range_(range), title_(title)
|
range_(range), title_(title)
|
||||||
{
|
{
|
||||||
@ -152,11 +155,6 @@ public:
|
|||||||
this, id_);
|
this, id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void cancel() override {
|
|
||||||
update(max(), "Abort");
|
|
||||||
ProgressIndicator::cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void state(float val) override {
|
virtual void state(float val) override {
|
||||||
state(static_cast<unsigned>(val));
|
state(static_cast<unsigned>(val));
|
||||||
}
|
}
|
||||||
@ -171,26 +169,28 @@ public:
|
|||||||
} else _state(st);
|
} else _state(st);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void message(const wxString & msg) override {
|
virtual void message(const std::string & msg) override {
|
||||||
message_ = msg;
|
message_ = _(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void messageFmt(const wxString& fmt, ...) {
|
virtual void messageFmt(const std::string& fmt, ...) {
|
||||||
va_list arglist;
|
va_list arglist;
|
||||||
va_start(arglist, fmt);
|
va_start(arglist, fmt);
|
||||||
message_ = wxString::Format(wxString(fmt), arglist);
|
message_ = wxString::Format(_(fmt), arglist);
|
||||||
va_end(arglist);
|
va_end(arglist);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void title(const wxString & title) override {
|
virtual void title(const std::string & title) override {
|
||||||
title_ = title;
|
title_ = _(title);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
AppControllerBoilerplate::ProgresIndicatorPtr
|
AppControllerBoilerplate::ProgresIndicatorPtr
|
||||||
AppControllerBoilerplate::create_progress_indicator(
|
AppControllerBoilerplate::create_progress_indicator(
|
||||||
unsigned statenum, const wxString& title, const wxString& firstmsg) const
|
unsigned statenum,
|
||||||
|
const std::string& title,
|
||||||
|
const std::string& firstmsg) const
|
||||||
{
|
{
|
||||||
auto pri =
|
auto pri =
|
||||||
std::make_shared<GuiProgressIndicator>(statenum, title, firstmsg);
|
std::make_shared<GuiProgressIndicator>(statenum, title, firstmsg);
|
||||||
@ -203,10 +203,10 @@ AppControllerBoilerplate::create_progress_indicator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
AppControllerBoilerplate::ProgresIndicatorPtr
|
AppControllerBoilerplate::ProgresIndicatorPtr
|
||||||
AppControllerBoilerplate::create_progress_indicator(unsigned statenum,
|
AppControllerBoilerplate::create_progress_indicator(
|
||||||
const wxString &title) const
|
unsigned statenum, const std::string &title) const
|
||||||
{
|
{
|
||||||
return create_progress_indicator(statenum, title, wxString());
|
return create_progress_indicator(statenum, title, std::string());
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -214,7 +214,7 @@ namespace {
|
|||||||
class Wrapper: public ProgressIndicator, public wxEvtHandler {
|
class Wrapper: public ProgressIndicator, public wxEvtHandler {
|
||||||
ProgressStatusBar *sbar_;
|
ProgressStatusBar *sbar_;
|
||||||
using Base = ProgressIndicator;
|
using Base = ProgressIndicator;
|
||||||
std::string message_;
|
wxString message_;
|
||||||
AppControllerBoilerplate& ctl_;
|
AppControllerBoilerplate& ctl_;
|
||||||
|
|
||||||
void showProgress(bool show = true) {
|
void showProgress(bool show = true) {
|
||||||
@ -271,18 +271,18 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void message(const wxString & msg) override {
|
virtual void message(const std::string & msg) override {
|
||||||
message_ = msg;
|
message_ = _(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void message_fmt(const wxString& fmt, ...) override {
|
virtual void message_fmt(const std::string& fmt, ...) override {
|
||||||
va_list arglist;
|
va_list arglist;
|
||||||
va_start(arglist, fmt);
|
va_start(arglist, fmt);
|
||||||
message_ = wxString::Format(fmt, arglist);
|
message_ = wxString::Format(_(fmt), arglist);
|
||||||
va_end(arglist);
|
va_end(arglist);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void title(const wxString & /*title*/) override {}
|
virtual void title(const std::string & /*title*/) override {}
|
||||||
|
|
||||||
virtual void on_cancel(CancelFn fn) override {
|
virtual void on_cancel(CancelFn fn) override {
|
||||||
sbar_->set_cancel_callback(fn);
|
sbar_->set_cancel_callback(fn);
|
||||||
|
@ -648,7 +648,7 @@ std::vector<int> GLVolumeCollection::load_object(
|
|||||||
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
||||||
|
|
||||||
int extruder_id = -1;
|
int extruder_id = -1;
|
||||||
if (!model_volume->modifier)
|
if (model_volume->is_model_part())
|
||||||
{
|
{
|
||||||
extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0;
|
extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0;
|
||||||
if (extruder_id == 0)
|
if (extruder_id == 0)
|
||||||
@ -661,7 +661,16 @@ std::vector<int> GLVolumeCollection::load_object(
|
|||||||
volumes_idx.push_back(int(this->volumes.size()));
|
volumes_idx.push_back(int(this->volumes.size()));
|
||||||
float color[4];
|
float color[4];
|
||||||
memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
|
memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
|
||||||
color[3] = model_volume->modifier ? 0.5f : 1.f;
|
if (model_volume->is_support_blocker()) {
|
||||||
|
color[0] = 1.0f;
|
||||||
|
color[1] = 0.2f;
|
||||||
|
color[2] = 0.2f;
|
||||||
|
} else if (model_volume->is_support_enforcer()) {
|
||||||
|
color[0] = 0.2f;
|
||||||
|
color[1] = 0.2f;
|
||||||
|
color[2] = 1.0f;
|
||||||
|
}
|
||||||
|
color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
|
||||||
this->volumes.emplace_back(new GLVolume(color));
|
this->volumes.emplace_back(new GLVolume(color));
|
||||||
GLVolume &v = *this->volumes.back();
|
GLVolume &v = *this->volumes.back();
|
||||||
if (use_VBOs)
|
if (use_VBOs)
|
||||||
@ -675,16 +684,20 @@ std::vector<int> GLVolumeCollection::load_object(
|
|||||||
v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx;
|
v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx;
|
||||||
v.set_select_group_id(select_by);
|
v.set_select_group_id(select_by);
|
||||||
v.set_drag_group_id(drag_by);
|
v.set_drag_group_id(drag_by);
|
||||||
if (!model_volume->modifier)
|
if (model_volume->is_model_part())
|
||||||
{
|
{
|
||||||
v.set_convex_hull(model_volume->get_convex_hull());
|
v.set_convex_hull(model_volume->get_convex_hull());
|
||||||
v.layer_height_texture = layer_height_texture;
|
v.layer_height_texture = layer_height_texture;
|
||||||
if (extruder_id != -1)
|
if (extruder_id != -1)
|
||||||
v.extruder_id = extruder_id;
|
v.extruder_id = extruder_id;
|
||||||
}
|
}
|
||||||
v.is_modifier = model_volume->modifier;
|
v.is_modifier = ! model_volume->is_model_part();
|
||||||
v.shader_outside_printer_detection_enabled = !model_volume->modifier;
|
v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
v.set_offset(instance->get_offset());
|
||||||
|
#else
|
||||||
v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0));
|
v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0));
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
v.set_rotation(instance->rotation);
|
v.set_rotation(instance->rotation);
|
||||||
v.set_scaling_factor(instance->scaling_factor);
|
v.set_scaling_factor(instance->scaling_factor);
|
||||||
}
|
}
|
||||||
@ -2164,6 +2177,11 @@ int _3DScene::get_first_volume_id(wxGLCanvas* canvas, int obj_idx)
|
|||||||
return s_canvas_mgr.get_first_volume_id(canvas, obj_idx);
|
return s_canvas_mgr.get_first_volume_id(canvas, obj_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int _3DScene::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx)
|
||||||
|
{
|
||||||
|
return s_canvas_mgr.get_in_object_volume_id(canvas, scene_vol_idx);
|
||||||
|
}
|
||||||
|
|
||||||
void _3DScene::reload_scene(wxGLCanvas* canvas, bool force)
|
void _3DScene::reload_scene(wxGLCanvas* canvas, bool force)
|
||||||
{
|
{
|
||||||
s_canvas_mgr.reload_scene(canvas, force);
|
s_canvas_mgr.reload_scene(canvas, force);
|
||||||
|
@ -577,6 +577,7 @@ public:
|
|||||||
static std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
|
static std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
|
||||||
|
|
||||||
static int get_first_volume_id(wxGLCanvas* canvas, int obj_idx);
|
static int get_first_volume_id(wxGLCanvas* canvas, int obj_idx);
|
||||||
|
static int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx);
|
||||||
|
|
||||||
static void reload_scene(wxGLCanvas* canvas, bool force);
|
static void reload_scene(wxGLCanvas* canvas, bool force);
|
||||||
|
|
||||||
|
@ -60,6 +60,14 @@ void AppConfig::set_defaults()
|
|||||||
|
|
||||||
if (get("remember_output_path").empty())
|
if (get("remember_output_path").empty())
|
||||||
set("remember_output_path", "1");
|
set("remember_output_path", "1");
|
||||||
|
|
||||||
|
// Remove legacy window positions/sizes
|
||||||
|
erase("", "main_frame_maximized");
|
||||||
|
erase("", "main_frame_pos");
|
||||||
|
erase("", "main_frame_size");
|
||||||
|
erase("", "object_settings_maximized");
|
||||||
|
erase("", "object_settings_pos");
|
||||||
|
erase("", "object_settings_size");
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppConfig::load()
|
void AppConfig::load()
|
||||||
|
@ -72,6 +72,14 @@ public:
|
|||||||
bool has(const std::string &key) const
|
bool has(const std::string &key) const
|
||||||
{ return this->has("", key); }
|
{ return this->has("", key); }
|
||||||
|
|
||||||
|
void erase(const std::string §ion, const std::string &key)
|
||||||
|
{
|
||||||
|
auto it = m_storage.find(section);
|
||||||
|
if (it != m_storage.end()) {
|
||||||
|
it->second.erase(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void clear_section(const std::string §ion)
|
void clear_section(const std::string §ion)
|
||||||
{ m_storage[section].clear(); }
|
{ m_storage[section].clear(); }
|
||||||
|
|
||||||
|
@ -409,11 +409,10 @@ PageFirmware::PageFirmware(ConfigWizard *parent) :
|
|||||||
|
|
||||||
void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
|
void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
|
||||||
{
|
{
|
||||||
ConfigOptionEnum<GCodeFlavor> opt;
|
|
||||||
|
|
||||||
auto sel = gcode_picker->GetSelection();
|
auto sel = gcode_picker->GetSelection();
|
||||||
if (sel != wxNOT_FOUND && opt.deserialize(gcode_picker->GetString(sel).ToStdString())) {
|
if (sel >= 0 && sel < gcode_opt.enum_labels.size()) {
|
||||||
config.set_key_value("gcode_flavor", &opt);
|
auto *opt = new ConfigOptionEnum<GCodeFlavor>(static_cast<GCodeFlavor>(sel));
|
||||||
|
config.set_key_value("gcode_flavor", opt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -871,10 +870,11 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
|
|||||||
// If the screen is smaller, resize wizrad to match, which will enable scrollbars.
|
// If the screen is smaller, resize wizrad to match, which will enable scrollbars.
|
||||||
auto wizard_size = GetSize();
|
auto wizard_size = GetSize();
|
||||||
unsigned width, height;
|
unsigned width, height;
|
||||||
GUI::get_current_screen_size(width, height);
|
if (GUI::get_current_screen_size(this, width, height)) {
|
||||||
wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN)));
|
wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN)));
|
||||||
wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN)));
|
wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN)));
|
||||||
SetMinSize(wizard_size);
|
SetMinSize(wizard_size);
|
||||||
|
}
|
||||||
Fit();
|
Fit();
|
||||||
|
|
||||||
p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); });
|
p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); });
|
||||||
|
@ -367,7 +367,7 @@ void FirmwareDialog::priv::wait_for_mmu_bootloader(unsigned retries)
|
|||||||
|
|
||||||
auto ports = Utils::scan_serial_ports_extended();
|
auto ports = Utils::scan_serial_ports_extended();
|
||||||
ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
|
ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
|
||||||
return port.id_vendor != USB_VID_PRUSA && port.id_product != USB_PID_MMU_BOOT;
|
return port.id_vendor != USB_VID_PRUSA || port.id_product != USB_PID_MMU_BOOT;
|
||||||
}), ports.end());
|
}), ports.end());
|
||||||
|
|
||||||
if (ports.size() == 1) {
|
if (ports.size() == 1) {
|
||||||
@ -390,23 +390,22 @@ void FirmwareDialog::priv::mmu_reboot(const SerialPortInfo &port)
|
|||||||
|
|
||||||
void FirmwareDialog::priv::lookup_port_mmu()
|
void FirmwareDialog::priv::lookup_port_mmu()
|
||||||
{
|
{
|
||||||
|
static const auto msg_not_found =
|
||||||
|
"The Multi Material Control device was not found.\n"
|
||||||
|
"If the device is connected, please press the Reset button next to the USB connector ...";
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ...";
|
BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ...";
|
||||||
|
|
||||||
auto ports = Utils::scan_serial_ports_extended();
|
auto ports = Utils::scan_serial_ports_extended();
|
||||||
ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
|
ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
|
||||||
return port.id_vendor != USB_VID_PRUSA &&
|
return port.id_vendor != USB_VID_PRUSA ||
|
||||||
port.id_product != USB_PID_MMU_BOOT &&
|
port.id_product != USB_PID_MMU_BOOT &&
|
||||||
port.id_product != USB_PID_MMU_APP;
|
port.id_product != USB_PID_MMU_APP;
|
||||||
}), ports.end());
|
}), ports.end());
|
||||||
|
|
||||||
if (ports.size() == 0) {
|
if (ports.size() == 0) {
|
||||||
BOOST_LOG_TRIVIAL(info) << "MMU 2.0 device not found, asking the user to press Reset and waiting for the device to show up ...";
|
BOOST_LOG_TRIVIAL(info) << "MMU 2.0 device not found, asking the user to press Reset and waiting for the device to show up ...";
|
||||||
|
queue_status(_(L(msg_not_found)));
|
||||||
queue_status(_(L(
|
|
||||||
"The Multi Material Control device was not found.\n"
|
|
||||||
"If the device is connected, please press the Reset button next to the USB connector ..."
|
|
||||||
)));
|
|
||||||
|
|
||||||
wait_for_mmu_bootloader(30);
|
wait_for_mmu_bootloader(30);
|
||||||
} else if (ports.size() > 1) {
|
} else if (ports.size() > 1) {
|
||||||
BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found";
|
BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found";
|
||||||
@ -417,6 +416,13 @@ void FirmwareDialog::priv::lookup_port_mmu()
|
|||||||
BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % ports[0].port;
|
BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % ports[0].port;
|
||||||
mmu_reboot(ports[0]);
|
mmu_reboot(ports[0]);
|
||||||
wait_for_mmu_bootloader(10);
|
wait_for_mmu_bootloader(10);
|
||||||
|
|
||||||
|
if (! port) {
|
||||||
|
// The device in bootloader mode was not found, inform the user and wait some more...
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "MMU 2.0 bootloader device not found after reboot, asking the user to press Reset and waiting for the device to show up ...";
|
||||||
|
queue_status(_(L(msg_not_found)));
|
||||||
|
wait_for_mmu_bootloader(30);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
port = ports[0];
|
port = ports[0];
|
||||||
}
|
}
|
||||||
@ -702,7 +708,8 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
|||||||
panel->SetSizer(vsizer);
|
panel->SetSizer(vsizer);
|
||||||
|
|
||||||
auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
|
auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
|
||||||
p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY);
|
p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString, wxFileSelectorPromptStr,
|
||||||
|
"Hex files (*.hex)|*.hex|All files|*.*");
|
||||||
|
|
||||||
auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
|
auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
|
||||||
p->port_picker = new wxComboBox(panel, wxID_ANY);
|
p->port_picker = new wxComboBox(panel, wxID_ANY);
|
||||||
|
@ -1142,8 +1142,10 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent)
|
|||||||
if (!gizmo->init())
|
if (!gizmo->init())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#if !ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
// temporary disable z grabber
|
// temporary disable z grabber
|
||||||
gizmo->disable_grabber(2);
|
gizmo->disable_grabber(2);
|
||||||
|
#endif // !ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
|
||||||
m_gizmos.insert(GizmosMap::value_type(Move, gizmo));
|
m_gizmos.insert(GizmosMap::value_type(Move, gizmo));
|
||||||
|
|
||||||
@ -2377,7 +2379,11 @@ void GLCanvas3D::update_gizmos_data()
|
|||||||
ModelInstance* model_instance = model_object->instances[0];
|
ModelInstance* model_instance = model_object->instances[0];
|
||||||
if (model_instance != nullptr)
|
if (model_instance != nullptr)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
m_gizmos.set_position(model_instance->get_offset());
|
||||||
|
#else
|
||||||
m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0));
|
m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0));
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
m_gizmos.set_scale(model_instance->scaling_factor);
|
m_gizmos.set_scale(model_instance->scaling_factor);
|
||||||
m_gizmos.set_angle_z(model_instance->rotation);
|
m_gizmos.set_angle_z(model_instance->rotation);
|
||||||
m_gizmos.set_flattening_data(model_object);
|
m_gizmos.set_flattening_data(model_object);
|
||||||
@ -2495,6 +2501,11 @@ int GLCanvas3D::get_first_volume_id(int obj_idx) const
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GLCanvas3D::get_in_object_volume_id(int scene_vol_idx) const
|
||||||
|
{
|
||||||
|
return ((0 <= scene_vol_idx) && (scene_vol_idx < (int)m_volumes.volumes.size())) ? m_volumes.volumes[scene_vol_idx]->volume_idx() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
void GLCanvas3D::reload_scene(bool force)
|
void GLCanvas3D::reload_scene(bool force)
|
||||||
{
|
{
|
||||||
if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr))
|
if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr))
|
||||||
@ -5361,8 +5372,12 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
|
|||||||
ModelObject* model_object = m_model->objects[obj_idx];
|
ModelObject* model_object = m_model->objects[obj_idx];
|
||||||
if (model_object != nullptr)
|
if (model_object != nullptr)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
model_object->instances[instance_idx]->set_offset(volume->get_offset());
|
||||||
|
#else
|
||||||
const Vec3d& offset = volume->get_offset();
|
const Vec3d& offset = volume->get_offset();
|
||||||
model_object->instances[instance_idx]->offset = Vec2d(offset(0), offset(1));
|
model_object->instances[instance_idx]->offset = Vec2d(offset(0), offset(1));
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
model_object->invalidate_bounding_box();
|
model_object->invalidate_bounding_box();
|
||||||
update_position_values();
|
update_position_values();
|
||||||
object_moved = true;
|
object_moved = true;
|
||||||
@ -5409,6 +5424,7 @@ void GLCanvas3D::_on_select(int volume_idx, int object_idx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_on_select_object_callback.call(obj_id, vol_id);
|
m_on_select_object_callback.call(obj_id, vol_id);
|
||||||
|
Slic3r::GUI::select_current_volume(obj_id, vol_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
|
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
|
||||||
|
@ -105,7 +105,6 @@ class GLCanvas3D
|
|||||||
void reset() { first_volumes.clear(); }
|
void reset() { first_volumes.clear(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
|
||||||
struct Camera
|
struct Camera
|
||||||
{
|
{
|
||||||
enum EType : unsigned char
|
enum EType : unsigned char
|
||||||
@ -441,7 +440,6 @@ public:
|
|||||||
void render(const GLCanvas3D& canvas) const;
|
void render(const GLCanvas3D& canvas) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
|
||||||
wxGLCanvas* m_canvas;
|
wxGLCanvas* m_canvas;
|
||||||
wxGLContext* m_context;
|
wxGLContext* m_context;
|
||||||
LegendTexture m_legend_texture;
|
LegendTexture m_legend_texture;
|
||||||
@ -605,6 +603,7 @@ public:
|
|||||||
std::vector<int> load_object(const Model& model, int obj_idx);
|
std::vector<int> load_object(const Model& model, int obj_idx);
|
||||||
|
|
||||||
int get_first_volume_id(int obj_idx) const;
|
int get_first_volume_id(int obj_idx) const;
|
||||||
|
int get_in_object_volume_id(int scene_vol_idx) const;
|
||||||
|
|
||||||
void reload_scene(bool force);
|
void reload_scene(bool force);
|
||||||
|
|
||||||
|
@ -548,6 +548,12 @@ int GLCanvas3DManager::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) cons
|
|||||||
return (it != m_canvases.end()) ? it->second->get_first_volume_id(obj_idx) : -1;
|
return (it != m_canvases.end()) ? it->second->get_first_volume_id(obj_idx) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GLCanvas3DManager::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) const
|
||||||
|
{
|
||||||
|
CanvasesMap::const_iterator it = _get_canvas(canvas);
|
||||||
|
return (it != m_canvases.end()) ? it->second->get_in_object_volume_id(scene_vol_idx) : -1;
|
||||||
|
}
|
||||||
|
|
||||||
void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force)
|
void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force)
|
||||||
{
|
{
|
||||||
CanvasesMap::iterator it = _get_canvas(canvas);
|
CanvasesMap::iterator it = _get_canvas(canvas);
|
||||||
|
@ -138,6 +138,7 @@ public:
|
|||||||
std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
|
std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
|
||||||
|
|
||||||
int get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const;
|
int get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const;
|
||||||
|
int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) const;
|
||||||
|
|
||||||
void reload_scene(wxGLCanvas* canvas, bool force);
|
void reload_scene(wxGLCanvas* canvas, bool force);
|
||||||
|
|
||||||
|
@ -20,90 +20,91 @@ static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f
|
|||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
// returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center
|
// returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center
|
||||||
// coordinates are local to the plane
|
// coordinates are local to the plane
|
||||||
Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center)
|
Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center)
|
||||||
|
{
|
||||||
|
Transform3d m = Transform3d::Identity();
|
||||||
|
m.translate(-center);
|
||||||
|
Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
|
||||||
|
return Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the intersection of the given ray with the plane parallel to plane XZ and passing through the given center
|
||||||
|
// coordinates are local to the plane
|
||||||
|
Vec3d intersection_on_plane_xz(const Linef3& ray, const Vec3d& center)
|
||||||
|
{
|
||||||
|
Transform3d m = Transform3d::Identity();
|
||||||
|
m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX()));
|
||||||
|
m.translate(-center);
|
||||||
|
Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
|
||||||
|
return Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the intersection of the given ray with the plane parallel to plane YZ and passing through the given center
|
||||||
|
// coordinates are local to the plane
|
||||||
|
Vec3d intersection_on_plane_yz(const Linef3& ray, const Vec3d& center)
|
||||||
|
{
|
||||||
|
Transform3d m = Transform3d::Identity();
|
||||||
|
m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY()));
|
||||||
|
m.translate(-center);
|
||||||
|
Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
|
||||||
|
|
||||||
|
return Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// return an index:
|
||||||
|
// 0 for plane XY
|
||||||
|
// 1 for plane XZ
|
||||||
|
// 2 for plane YZ
|
||||||
|
// which indicates which plane is best suited for intersecting the given unit vector
|
||||||
|
// giving precedence to the plane with the given index
|
||||||
|
unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_plane)
|
||||||
|
{
|
||||||
|
unsigned int ret = preferred_plane;
|
||||||
|
|
||||||
|
// 1st checks if the given vector is not parallel to the given preferred plane
|
||||||
|
double dot_to_normal = 0.0;
|
||||||
|
switch (ret)
|
||||||
{
|
{
|
||||||
Transform3d m = Transform3d::Identity();
|
case 0: // plane xy
|
||||||
m.translate(-center);
|
{
|
||||||
Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
|
dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitZ()));
|
||||||
return Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0);
|
break;
|
||||||
|
}
|
||||||
|
case 1: // plane xz
|
||||||
|
{
|
||||||
|
dot_to_normal = std::abs(unit_vector.dot(-Vec3d::UnitY()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: // plane yz
|
||||||
|
{
|
||||||
|
dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitX()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the intersection of the given ray with the plane parallel to plane XZ and passing through the given center
|
// if almost parallel, select the plane whose normal direction is closest to the given vector direction,
|
||||||
// coordinates are local to the plane
|
// otherwise return the given preferred plane index
|
||||||
Vec3d intersection_on_plane_xz(const Linef3& ray, const Vec3d& center)
|
if (dot_to_normal < 0.1)
|
||||||
{
|
{
|
||||||
Transform3d m = Transform3d::Identity();
|
typedef std::map<double, unsigned int> ProjsMap;
|
||||||
m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX()));
|
ProjsMap projs_map;
|
||||||
m.translate(-center);
|
projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitZ())), 0)); // plane xy
|
||||||
Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
|
projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(-Vec3d::UnitY())), 1)); // plane xz
|
||||||
return Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1));
|
projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitX())), 2)); // plane yz
|
||||||
|
ret = projs_map.rbegin()->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the intersection of the given ray with the plane parallel to plane YZ and passing through the given center
|
return ret;
|
||||||
// coordinates are local to the plane
|
}
|
||||||
Vec3d intersection_on_plane_yz(const Linef3& ray, const Vec3d& center)
|
|
||||||
{
|
|
||||||
Transform3d m = Transform3d::Identity();
|
|
||||||
m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY()));
|
|
||||||
m.translate(-center);
|
|
||||||
Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
|
|
||||||
|
|
||||||
return Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// return an index:
|
|
||||||
// 0 for plane XY
|
|
||||||
// 1 for plane XZ
|
|
||||||
// 2 for plane YZ
|
|
||||||
// which indicates which plane is best suited for intersecting the given unit vector
|
|
||||||
// giving precedence to the plane with the given index
|
|
||||||
unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_plane)
|
|
||||||
{
|
|
||||||
unsigned int ret = preferred_plane;
|
|
||||||
|
|
||||||
// 1st checks if the given vector is not parallel to the given preferred plane
|
|
||||||
double dot_to_normal = 0.0;
|
|
||||||
switch (ret)
|
|
||||||
{
|
|
||||||
case 0: // plane xy
|
|
||||||
{
|
|
||||||
dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitZ()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1: // plane xz
|
|
||||||
{
|
|
||||||
dot_to_normal = std::abs(unit_vector.dot(-Vec3d::UnitY()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2: // plane yz
|
|
||||||
{
|
|
||||||
dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitX()));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if almost parallel, select the plane whose normal direction is closest to the given vector direction,
|
|
||||||
// otherwise return the given preferred plane index
|
|
||||||
if (dot_to_normal < 0.1)
|
|
||||||
{
|
|
||||||
typedef std::map<double, unsigned int> ProjsMap;
|
|
||||||
ProjsMap projs_map;
|
|
||||||
projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitZ())), 0)); // plane xy
|
|
||||||
projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(-Vec3d::UnitY())), 1)); // plane xz
|
|
||||||
projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitX())), 2)); // plane yz
|
|
||||||
ret = projs_map.rbegin()->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float GLGizmoBase::Grabber::HalfSize = 2.0f;
|
const float GLGizmoBase::Grabber::SizeFactor = 0.025f;
|
||||||
|
const float GLGizmoBase::Grabber::MinHalfSize = 1.5f;
|
||||||
const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f;
|
const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f;
|
||||||
|
|
||||||
GLGizmoBase::Grabber::Grabber()
|
GLGizmoBase::Grabber::Grabber()
|
||||||
@ -117,7 +118,7 @@ GLGizmoBase::Grabber::Grabber()
|
|||||||
color[2] = 1.0f;
|
color[2] = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoBase::Grabber::render(bool hover) const
|
void GLGizmoBase::Grabber::render(bool hover, const BoundingBoxf3& box) const
|
||||||
{
|
{
|
||||||
float render_color[3];
|
float render_color[3];
|
||||||
if (hover)
|
if (hover)
|
||||||
@ -129,12 +130,15 @@ void GLGizmoBase::Grabber::render(bool hover) const
|
|||||||
else
|
else
|
||||||
::memcpy((void*)render_color, (const void*)color, 3 * sizeof(float));
|
::memcpy((void*)render_color, (const void*)color, 3 * sizeof(float));
|
||||||
|
|
||||||
render(render_color, true);
|
render(box, render_color, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoBase::Grabber::render(const float* render_color, bool use_lighting) const
|
void GLGizmoBase::Grabber::render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const
|
||||||
{
|
{
|
||||||
float half_size = dragging ? HalfSize * DraggingScaleFactor : HalfSize;
|
float max_size = (float)box.max_size();
|
||||||
|
float half_size = dragging ? max_size * SizeFactor * DraggingScaleFactor : max_size * SizeFactor;
|
||||||
|
half_size = std::max(half_size, MinHalfSize);
|
||||||
|
|
||||||
if (use_lighting)
|
if (use_lighting)
|
||||||
::glEnable(GL_LIGHTING);
|
::glEnable(GL_LIGHTING);
|
||||||
|
|
||||||
@ -291,16 +295,16 @@ float GLGizmoBase::picking_color_component(unsigned int id) const
|
|||||||
return (float)color / 255.0f;
|
return (float)color / 255.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoBase::render_grabbers() const
|
void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
|
||||||
{
|
{
|
||||||
for (int i = 0; i < (int)m_grabbers.size(); ++i)
|
for (int i = 0; i < (int)m_grabbers.size(); ++i)
|
||||||
{
|
{
|
||||||
if (m_grabbers[i].enabled)
|
if (m_grabbers[i].enabled)
|
||||||
m_grabbers[i].render(m_hover_id == i);
|
m_grabbers[i].render((m_hover_id == i), box);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoBase::render_grabbers_for_picking() const
|
void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
|
for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
|
||||||
{
|
{
|
||||||
@ -309,7 +313,7 @@ void GLGizmoBase::render_grabbers_for_picking() const
|
|||||||
m_grabbers[i].color[0] = 1.0f;
|
m_grabbers[i].color[0] = 1.0f;
|
||||||
m_grabbers[i].color[1] = 1.0f;
|
m_grabbers[i].color[1] = 1.0f;
|
||||||
m_grabbers[i].color[2] = picking_color_component(i);
|
m_grabbers[i].color[2] = picking_color_component(i);
|
||||||
m_grabbers[i].render_for_picking();
|
m_grabbers[i].render_for_picking(box);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -440,7 +444,7 @@ void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
|
|||||||
if (m_hover_id != -1)
|
if (m_hover_id != -1)
|
||||||
render_angle();
|
render_angle();
|
||||||
|
|
||||||
render_grabber();
|
render_grabber(box);
|
||||||
|
|
||||||
::glPopMatrix();
|
::glPopMatrix();
|
||||||
}
|
}
|
||||||
@ -452,7 +456,7 @@ void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const
|
|||||||
::glPushMatrix();
|
::glPushMatrix();
|
||||||
|
|
||||||
transform_to_local();
|
transform_to_local();
|
||||||
render_grabbers_for_picking();
|
render_grabbers_for_picking(box);
|
||||||
|
|
||||||
::glPopMatrix();
|
::glPopMatrix();
|
||||||
}
|
}
|
||||||
@ -544,7 +548,7 @@ void GLGizmoRotate::render_angle() const
|
|||||||
::glEnd();
|
::glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoRotate::render_grabber() const
|
void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
|
||||||
{
|
{
|
||||||
double grabber_radius = (double)(m_radius + GrabberOffset);
|
double grabber_radius = (double)(m_radius + GrabberOffset);
|
||||||
m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0);
|
m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0);
|
||||||
@ -558,7 +562,7 @@ void GLGizmoRotate::render_grabber() const
|
|||||||
::glEnd();
|
::glEnd();
|
||||||
|
|
||||||
::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float));
|
::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float));
|
||||||
render_grabbers();
|
render_grabbers(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoRotate::transform_to_local() const
|
void GLGizmoRotate::transform_to_local() const
|
||||||
@ -689,6 +693,7 @@ void GLGizmoRotate3D::on_render(const BoundingBoxf3& box) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
const float GLGizmoScale3D::Offset = 5.0f;
|
const float GLGizmoScale3D::Offset = 5.0f;
|
||||||
|
const Vec3d GLGizmoScale3D::OffsetVec = (double)GLGizmoScale3D::Offset * Vec3d::Ones();
|
||||||
|
|
||||||
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent)
|
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent)
|
||||||
: GLGizmoBase(parent)
|
: GLGizmoBase(parent)
|
||||||
@ -738,7 +743,7 @@ void GLGizmoScale3D::on_start_dragging(const BoundingBoxf3& box)
|
|||||||
{
|
{
|
||||||
m_starting_drag_position = m_grabbers[m_hover_id].center;
|
m_starting_drag_position = m_grabbers[m_hover_id].center;
|
||||||
m_show_starting_box = true;
|
m_show_starting_box = true;
|
||||||
m_starting_box = box;
|
m_starting_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,9 +777,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
|
|||||||
|
|
||||||
::glEnable(GL_DEPTH_TEST);
|
::glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
Vec3d offset_vec = (double)Offset * Vec3d::Ones();
|
m_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec);
|
||||||
|
|
||||||
m_box = BoundingBoxf3(box.min - offset_vec, box.max + offset_vec);
|
|
||||||
const Vec3d& center = m_box.center();
|
const Vec3d& center = m_box.center();
|
||||||
|
|
||||||
// x axis
|
// x axis
|
||||||
@ -829,7 +832,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
|
|||||||
render_grabbers_connection(4, 5);
|
render_grabbers_connection(4, 5);
|
||||||
}
|
}
|
||||||
// draw grabbers
|
// draw grabbers
|
||||||
render_grabbers();
|
render_grabbers(m_box);
|
||||||
}
|
}
|
||||||
else if ((m_hover_id == 0) || (m_hover_id == 1))
|
else if ((m_hover_id == 0) || (m_hover_id == 1))
|
||||||
{
|
{
|
||||||
@ -846,8 +849,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
|
|||||||
::glColor3fv(m_grabbers[0].color);
|
::glColor3fv(m_grabbers[0].color);
|
||||||
render_grabbers_connection(0, 1);
|
render_grabbers_connection(0, 1);
|
||||||
// draw grabbers
|
// draw grabbers
|
||||||
m_grabbers[0].render(true);
|
m_grabbers[0].render(true, m_box);
|
||||||
m_grabbers[1].render(true);
|
m_grabbers[1].render(true, m_box);
|
||||||
}
|
}
|
||||||
else if ((m_hover_id == 2) || (m_hover_id == 3))
|
else if ((m_hover_id == 2) || (m_hover_id == 3))
|
||||||
{
|
{
|
||||||
@ -864,8 +867,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
|
|||||||
::glColor3fv(m_grabbers[2].color);
|
::glColor3fv(m_grabbers[2].color);
|
||||||
render_grabbers_connection(2, 3);
|
render_grabbers_connection(2, 3);
|
||||||
// draw grabbers
|
// draw grabbers
|
||||||
m_grabbers[2].render(true);
|
m_grabbers[2].render(true, m_box);
|
||||||
m_grabbers[3].render(true);
|
m_grabbers[3].render(true, m_box);
|
||||||
}
|
}
|
||||||
else if ((m_hover_id == 4) || (m_hover_id == 5))
|
else if ((m_hover_id == 4) || (m_hover_id == 5))
|
||||||
{
|
{
|
||||||
@ -882,8 +885,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
|
|||||||
::glColor3fv(m_grabbers[4].color);
|
::glColor3fv(m_grabbers[4].color);
|
||||||
render_grabbers_connection(4, 5);
|
render_grabbers_connection(4, 5);
|
||||||
// draw grabbers
|
// draw grabbers
|
||||||
m_grabbers[4].render(true);
|
m_grabbers[4].render(true, m_box);
|
||||||
m_grabbers[5].render(true);
|
m_grabbers[5].render(true, m_box);
|
||||||
}
|
}
|
||||||
else if (m_hover_id >= 6)
|
else if (m_hover_id >= 6)
|
||||||
{
|
{
|
||||||
@ -899,7 +902,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
|
|||||||
// draw grabbers
|
// draw grabbers
|
||||||
for (int i = 6; i < 10; ++i)
|
for (int i = 6; i < 10; ++i)
|
||||||
{
|
{
|
||||||
m_grabbers[i].render(true);
|
m_grabbers[i].render(true, m_box);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -908,7 +911,7 @@ void GLGizmoScale3D::on_render_for_picking(const BoundingBoxf3& box) const
|
|||||||
{
|
{
|
||||||
::glDisable(GL_DEPTH_TEST);
|
::glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
render_grabbers_for_picking();
|
render_grabbers_for_picking(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoScale3D::render_box(const BoundingBoxf3& box) const
|
void GLGizmoScale3D::render_box(const BoundingBoxf3& box) const
|
||||||
@ -1029,6 +1032,7 @@ GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent)
|
|||||||
, m_position(Vec3d::Zero())
|
, m_position(Vec3d::Zero())
|
||||||
, m_starting_drag_position(Vec3d::Zero())
|
, m_starting_drag_position(Vec3d::Zero())
|
||||||
, m_starting_box_center(Vec3d::Zero())
|
, m_starting_box_center(Vec3d::Zero())
|
||||||
|
, m_starting_box_bottom_center(Vec3d::Zero())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1062,17 +1066,19 @@ void GLGizmoMove3D::on_start_dragging(const BoundingBoxf3& box)
|
|||||||
{
|
{
|
||||||
m_starting_drag_position = m_grabbers[m_hover_id].center;
|
m_starting_drag_position = m_grabbers[m_hover_id].center;
|
||||||
m_starting_box_center = box.center();
|
m_starting_box_center = box.center();
|
||||||
|
m_starting_box_bottom_center = box.center();
|
||||||
|
m_starting_box_bottom_center(2) = box.min(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoMove3D::on_update(const Linef3& mouse_ray)
|
void GLGizmoMove3D::on_update(const Linef3& mouse_ray)
|
||||||
{
|
{
|
||||||
if (m_hover_id == 0)
|
if (m_hover_id == 0)
|
||||||
m_position(0) = 2.0 * m_starting_box_center(0) + calc_displacement(1, mouse_ray) - m_starting_drag_position(0);
|
m_position(0) = 2.0 * m_starting_box_center(0) + calc_projection(X, 1, mouse_ray) - m_starting_drag_position(0);
|
||||||
else if (m_hover_id == 1)
|
else if (m_hover_id == 1)
|
||||||
m_position(1) = 2.0 * m_starting_box_center(1) + calc_displacement(2, mouse_ray) - m_starting_drag_position(1);
|
m_position(1) = 2.0 * m_starting_box_center(1) + calc_projection(Y, 2, mouse_ray) - m_starting_drag_position(1);
|
||||||
else if (m_hover_id == 2)
|
else if (m_hover_id == 2)
|
||||||
m_position(2) = 2.0 * m_starting_box_center(2) + calc_displacement(1, mouse_ray) - m_starting_drag_position(2);
|
m_position(2) = 2.0 * m_starting_box_bottom_center(2) + calc_projection(Z, 1, mouse_ray) - m_starting_drag_position(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
|
void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
|
||||||
@ -1118,7 +1124,7 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// draw grabbers
|
// draw grabbers
|
||||||
render_grabbers();
|
render_grabbers(box);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1130,7 +1136,7 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
|
|||||||
::glEnd();
|
::glEnd();
|
||||||
|
|
||||||
// draw grabber
|
// draw grabber
|
||||||
m_grabbers[m_hover_id].render(true);
|
m_grabbers[m_hover_id].render(true, box);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1138,43 +1144,43 @@ void GLGizmoMove3D::on_render_for_picking(const BoundingBoxf3& box) const
|
|||||||
{
|
{
|
||||||
::glDisable(GL_DEPTH_TEST);
|
::glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
render_grabbers_for_picking();
|
render_grabbers_for_picking(box);
|
||||||
}
|
}
|
||||||
|
|
||||||
double GLGizmoMove3D::calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const
|
double GLGizmoMove3D::calc_projection(Axis axis, unsigned int preferred_plane_id, const Linef3& mouse_ray) const
|
||||||
{
|
{
|
||||||
double displacement = 0.0;
|
double projection = 0.0;
|
||||||
|
|
||||||
Vec3d starting_vec = m_starting_drag_position - m_starting_box_center;
|
Vec3d starting_vec = (axis == Z) ? m_starting_drag_position - m_starting_box_bottom_center : m_starting_drag_position - m_starting_box_center;
|
||||||
double len_starting_vec = starting_vec.norm();
|
double len_starting_vec = starting_vec.norm();
|
||||||
if (len_starting_vec == 0.0)
|
if (len_starting_vec == 0.0)
|
||||||
return displacement;
|
return projection;
|
||||||
|
|
||||||
Vec3d starting_vec_dir = starting_vec.normalized();
|
Vec3d starting_vec_dir = starting_vec.normalized();
|
||||||
Vec3d mouse_dir = mouse_ray.unit_vector();
|
Vec3d mouse_dir = mouse_ray.unit_vector();
|
||||||
|
|
||||||
unsigned int plane_id = select_best_plane(mouse_dir, preferred_plane_id);
|
unsigned int plane_id = select_best_plane(mouse_dir, preferred_plane_id);
|
||||||
|
|
||||||
switch (plane_id)
|
switch (plane_id)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
displacement = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, m_starting_box_center));
|
projection = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
displacement = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, m_starting_box_center));
|
projection = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
displacement = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, m_starting_box_center));
|
projection = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return displacement;
|
return projection;
|
||||||
}
|
}
|
||||||
|
|
||||||
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent)
|
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent)
|
||||||
@ -1230,10 +1236,19 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const
|
|||||||
else
|
else
|
||||||
::glColor4f(0.9f, 0.9f, 0.9f, 0.5f);
|
::glColor4f(0.9f, 0.9f, 0.9f, 0.5f);
|
||||||
|
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
for (Vec3d offset : m_instances_positions) {
|
||||||
|
offset += dragged_offset;
|
||||||
|
#else
|
||||||
for (Vec2d offset : m_instances_positions) {
|
for (Vec2d offset : m_instances_positions) {
|
||||||
offset += to_2d(dragged_offset);
|
offset += to_2d(dragged_offset);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
::glPushMatrix();
|
::glPushMatrix();
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
::glTranslated(offset(0), offset(1), offset(2));
|
||||||
|
#else
|
||||||
::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f);
|
::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
::glBegin(GL_POLYGON);
|
::glBegin(GL_POLYGON);
|
||||||
for (const Vec3d& vertex : m_planes[i].vertices)
|
for (const Vec3d& vertex : m_planes[i].vertices)
|
||||||
::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2));
|
::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2));
|
||||||
@ -1252,9 +1267,17 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const
|
|||||||
for (unsigned int i = 0; i < m_planes.size(); ++i)
|
for (unsigned int i = 0; i < m_planes.size(); ++i)
|
||||||
{
|
{
|
||||||
::glColor3f(1.0f, 1.0f, picking_color_component(i));
|
::glColor3f(1.0f, 1.0f, picking_color_component(i));
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
for (const Vec3d& offset : m_instances_positions) {
|
||||||
|
#else
|
||||||
for (const Vec2d& offset : m_instances_positions) {
|
for (const Vec2d& offset : m_instances_positions) {
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
::glPushMatrix();
|
::glPushMatrix();
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
::glTranslated(offset(0), offset(1), offset(2));
|
||||||
|
#else
|
||||||
::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f);
|
::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
::glBegin(GL_POLYGON);
|
::glBegin(GL_POLYGON);
|
||||||
for (const Vec3d& vertex : m_planes[i].vertices)
|
for (const Vec3d& vertex : m_planes[i].vertices)
|
||||||
::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2));
|
::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2));
|
||||||
@ -1272,7 +1295,11 @@ void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
|
|||||||
if (m_model_object && !m_model_object->instances.empty()) {
|
if (m_model_object && !m_model_object->instances.empty()) {
|
||||||
m_instances_positions.clear();
|
m_instances_positions.clear();
|
||||||
for (const auto* instance : m_model_object->instances)
|
for (const auto* instance : m_model_object->instances)
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
m_instances_positions.emplace_back(instance->get_offset());
|
||||||
|
#else
|
||||||
m_instances_positions.emplace_back(instance->offset);
|
m_instances_positions.emplace_back(instance->offset);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_plane_update_necessary())
|
if (is_plane_update_necessary())
|
||||||
|
@ -22,7 +22,8 @@ class GLGizmoBase
|
|||||||
protected:
|
protected:
|
||||||
struct Grabber
|
struct Grabber
|
||||||
{
|
{
|
||||||
static const float HalfSize;
|
static const float SizeFactor;
|
||||||
|
static const float MinHalfSize;
|
||||||
static const float DraggingScaleFactor;
|
static const float DraggingScaleFactor;
|
||||||
|
|
||||||
Vec3d center;
|
Vec3d center;
|
||||||
@ -33,11 +34,11 @@ protected:
|
|||||||
|
|
||||||
Grabber();
|
Grabber();
|
||||||
|
|
||||||
void render(bool hover) const;
|
void render(bool hover, const BoundingBoxf3& box) const;
|
||||||
void render_for_picking() const { render(color, false); }
|
void render_for_picking(const BoundingBoxf3& box) const { render(box, color, false); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void render(const float* render_color, bool use_lighting) const;
|
void render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const;
|
||||||
void render_face(float half_size) const;
|
void render_face(float half_size) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -109,8 +110,8 @@ protected:
|
|||||||
virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0;
|
virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0;
|
||||||
|
|
||||||
float picking_color_component(unsigned int id) const;
|
float picking_color_component(unsigned int id) const;
|
||||||
void render_grabbers() const;
|
void render_grabbers(const BoundingBoxf3& box) const;
|
||||||
void render_grabbers_for_picking() const;
|
void render_grabbers_for_picking(const BoundingBoxf3& box) const;
|
||||||
|
|
||||||
void set_tooltip(const std::string& tooltip) const;
|
void set_tooltip(const std::string& tooltip) const;
|
||||||
std::string format(float value, unsigned int decimals) const;
|
std::string format(float value, unsigned int decimals) const;
|
||||||
@ -163,7 +164,7 @@ private:
|
|||||||
void render_snap_radii() const;
|
void render_snap_radii() const;
|
||||||
void render_reference_radius() const;
|
void render_reference_radius() const;
|
||||||
void render_angle() const;
|
void render_angle() const;
|
||||||
void render_grabber() const;
|
void render_grabber(const BoundingBoxf3& box) const;
|
||||||
|
|
||||||
void transform_to_local() const;
|
void transform_to_local() const;
|
||||||
// returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate
|
// returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate
|
||||||
@ -234,6 +235,7 @@ protected:
|
|||||||
class GLGizmoScale3D : public GLGizmoBase
|
class GLGizmoScale3D : public GLGizmoBase
|
||||||
{
|
{
|
||||||
static const float Offset;
|
static const float Offset;
|
||||||
|
static const Vec3d OffsetVec;
|
||||||
|
|
||||||
mutable BoundingBoxf3 m_box;
|
mutable BoundingBoxf3 m_box;
|
||||||
|
|
||||||
@ -285,6 +287,7 @@ class GLGizmoMove3D : public GLGizmoBase
|
|||||||
Vec3d m_position;
|
Vec3d m_position;
|
||||||
Vec3d m_starting_drag_position;
|
Vec3d m_starting_drag_position;
|
||||||
Vec3d m_starting_box_center;
|
Vec3d m_starting_box_center;
|
||||||
|
Vec3d m_starting_box_bottom_center;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GLGizmoMove3D(GLCanvas3D& parent);
|
explicit GLGizmoMove3D(GLCanvas3D& parent);
|
||||||
@ -300,7 +303,7 @@ protected:
|
|||||||
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
|
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const;
|
double calc_projection(Axis axis, unsigned int preferred_plane_id, const Linef3& mouse_ray) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GLGizmoFlatten : public GLGizmoBase
|
class GLGizmoFlatten : public GLGizmoBase
|
||||||
@ -326,7 +329,11 @@ private:
|
|||||||
SourceDataSummary m_source_data;
|
SourceDataSummary m_source_data;
|
||||||
|
|
||||||
std::vector<PlaneData> m_planes;
|
std::vector<PlaneData> m_planes;
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
Pointf3s m_instances_positions;
|
||||||
|
#else
|
||||||
std::vector<Vec2d> m_instances_positions;
|
std::vector<Vec2d> m_instances_positions;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
Vec3d m_starting_center;
|
Vec3d m_starting_center;
|
||||||
const ModelObject* m_model_object = nullptr;
|
const ModelObject* m_model_object = nullptr;
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||||
@ -121,7 +122,6 @@ wxPanel *g_wxPlater = nullptr;
|
|||||||
AppConfig *g_AppConfig = nullptr;
|
AppConfig *g_AppConfig = nullptr;
|
||||||
PresetBundle *g_PresetBundle= nullptr;
|
PresetBundle *g_PresetBundle= nullptr;
|
||||||
PresetUpdater *g_PresetUpdater = nullptr;
|
PresetUpdater *g_PresetUpdater = nullptr;
|
||||||
_3DScene *g_3DScene = nullptr;
|
|
||||||
wxColour g_color_label_modified;
|
wxColour g_color_label_modified;
|
||||||
wxColour g_color_label_sys;
|
wxColour g_color_label_sys;
|
||||||
wxColour g_color_label_default;
|
wxColour g_color_label_default;
|
||||||
@ -141,14 +141,9 @@ wxButton* g_wiping_dialog_button = nullptr;
|
|||||||
//showed/hided controls according to the view mode
|
//showed/hided controls according to the view mode
|
||||||
wxWindow *g_right_panel = nullptr;
|
wxWindow *g_right_panel = nullptr;
|
||||||
wxBoxSizer *g_frequently_changed_parameters_sizer = nullptr;
|
wxBoxSizer *g_frequently_changed_parameters_sizer = nullptr;
|
||||||
wxBoxSizer *g_expert_mode_part_sizer = nullptr;
|
wxBoxSizer *g_info_sizer = nullptr;
|
||||||
wxBoxSizer *g_scrolled_window_sizer = nullptr;
|
|
||||||
wxBoxSizer *g_object_list_sizer = nullptr;
|
wxBoxSizer *g_object_list_sizer = nullptr;
|
||||||
wxButton *g_btn_export_gcode = nullptr;
|
std::vector<wxButton*> g_buttons;
|
||||||
wxButton *g_btn_export_stl = nullptr;
|
|
||||||
wxButton *g_btn_reslice = nullptr;
|
|
||||||
wxButton *g_btn_print = nullptr;
|
|
||||||
wxButton *g_btn_send_gcode = nullptr;
|
|
||||||
wxStaticBitmap *g_manifold_warning_icon = nullptr;
|
wxStaticBitmap *g_manifold_warning_icon = nullptr;
|
||||||
bool g_show_print_info = false;
|
bool g_show_print_info = false;
|
||||||
bool g_show_manifold_warning_icon = false;
|
bool g_show_manifold_warning_icon = false;
|
||||||
@ -241,27 +236,36 @@ void set_preset_updater(PresetUpdater *updater)
|
|||||||
g_PresetUpdater = updater;
|
g_PresetUpdater = updater;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_3DScene(_3DScene *scene)
|
enum ActionButtons
|
||||||
{
|
{
|
||||||
g_3DScene = scene;
|
abExportGCode,
|
||||||
}
|
abReslice,
|
||||||
|
abPrint,
|
||||||
|
abSendGCode,
|
||||||
|
};
|
||||||
|
|
||||||
void set_objects_from_perl( wxWindow* parent, wxBoxSizer *frequently_changed_parameters_sizer,
|
void set_objects_from_perl( wxWindow* parent,
|
||||||
wxBoxSizer *expert_mode_part_sizer, wxBoxSizer *scrolled_window_sizer,
|
wxBoxSizer *frequently_changed_parameters_sizer,
|
||||||
|
wxBoxSizer *info_sizer,
|
||||||
wxButton *btn_export_gcode,
|
wxButton *btn_export_gcode,
|
||||||
wxButton *btn_export_stl, wxButton *btn_reslice,
|
wxButton *btn_reslice,
|
||||||
wxButton *btn_print, wxButton *btn_send_gcode,
|
wxButton *btn_print,
|
||||||
|
wxButton *btn_send_gcode,
|
||||||
wxStaticBitmap *manifold_warning_icon)
|
wxStaticBitmap *manifold_warning_icon)
|
||||||
{
|
{
|
||||||
g_right_panel = parent;
|
g_right_panel = parent->GetParent();
|
||||||
g_frequently_changed_parameters_sizer = frequently_changed_parameters_sizer;
|
g_frequently_changed_parameters_sizer = frequently_changed_parameters_sizer;
|
||||||
g_expert_mode_part_sizer = expert_mode_part_sizer;
|
g_info_sizer = info_sizer;
|
||||||
g_scrolled_window_sizer = scrolled_window_sizer;
|
|
||||||
g_btn_export_gcode = btn_export_gcode;
|
g_buttons.push_back(btn_export_gcode);
|
||||||
g_btn_export_stl = btn_export_stl;
|
g_buttons.push_back(btn_reslice);
|
||||||
g_btn_reslice = btn_reslice;
|
g_buttons.push_back(btn_print);
|
||||||
g_btn_print = btn_print;
|
g_buttons.push_back(btn_send_gcode);
|
||||||
g_btn_send_gcode = btn_send_gcode;
|
|
||||||
|
// Update font style for buttons
|
||||||
|
for (auto btn : g_buttons)
|
||||||
|
btn->SetFont(bold_font());
|
||||||
|
|
||||||
g_manifold_warning_icon = manifold_warning_icon;
|
g_manifold_warning_icon = manifold_warning_icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,6 +277,15 @@ void set_show_print_info(bool show)
|
|||||||
void set_show_manifold_warning_icon(bool show)
|
void set_show_manifold_warning_icon(bool show)
|
||||||
{
|
{
|
||||||
g_show_manifold_warning_icon = show;
|
g_show_manifold_warning_icon = show;
|
||||||
|
if (!g_manifold_warning_icon)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// update manifold_warning_icon showing
|
||||||
|
if (show && !g_info_sizer->IsShown(static_cast<size_t>(0)))
|
||||||
|
g_show_manifold_warning_icon = false;
|
||||||
|
|
||||||
|
g_manifold_warning_icon->Show(g_show_manifold_warning_icon);
|
||||||
|
g_manifold_warning_icon->GetParent()->Layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_objects_list_sizer(wxBoxSizer *objects_list_sizer){
|
void set_objects_list_sizer(wxBoxSizer *objects_list_sizer){
|
||||||
@ -963,12 +976,11 @@ wxString from_u8(const std::string &str)
|
|||||||
return wxString::FromUTF8(str.c_str());
|
return wxString::FromUTF8(str.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_expert_mode_part( wxWindow* parent, wxBoxSizer* sizer,
|
void set_model_events_from_perl(Model &model,
|
||||||
Model &model,
|
int event_object_selection_changed,
|
||||||
int event_object_selection_changed,
|
int event_object_settings_changed,
|
||||||
int event_object_settings_changed,
|
int event_remove_object,
|
||||||
int event_remove_object,
|
int event_update_scene)
|
||||||
int event_update_scene)
|
|
||||||
{
|
{
|
||||||
set_event_object_selection_changed(event_object_selection_changed);
|
set_event_object_selection_changed(event_object_selection_changed);
|
||||||
set_event_object_settings_changed(event_object_settings_changed);
|
set_event_object_settings_changed(event_object_settings_changed);
|
||||||
@ -1105,7 +1117,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
|
|||||||
};
|
};
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
|
|
||||||
sizer->Add(optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 2);
|
sizer->Add(optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT, 2);
|
||||||
|
|
||||||
m_optgroups.push_back(optgroup);// ogFrequentlyChangingParameters
|
m_optgroups.push_back(optgroup);// ogFrequentlyChangingParameters
|
||||||
|
|
||||||
@ -1132,24 +1144,23 @@ void show_frequently_changed_parameters(bool show)
|
|||||||
|
|
||||||
void show_buttons(bool show)
|
void show_buttons(bool show)
|
||||||
{
|
{
|
||||||
g_btn_export_stl->Show(show);
|
g_buttons[abReslice]->Show(show);
|
||||||
g_btn_reslice->Show(show);
|
|
||||||
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
|
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
|
||||||
TabPrinter *tab = dynamic_cast<TabPrinter*>(g_wxTabPanel->GetPage(i));
|
TabPrinter *tab = dynamic_cast<TabPrinter*>(g_wxTabPanel->GetPage(i));
|
||||||
if (!tab)
|
if (!tab)
|
||||||
continue;
|
continue;
|
||||||
if (g_PresetBundle->printers.get_selected_preset().printer_technology() == ptFFF) {
|
if (g_PresetBundle->printers.get_selected_preset().printer_technology() == ptFFF) {
|
||||||
g_btn_print->Show(show && !tab->m_config->opt_string("serial_port").empty());
|
g_buttons[abPrint]->Show(show && !tab->m_config->opt_string("serial_port").empty());
|
||||||
g_btn_send_gcode->Show(show && !tab->m_config->opt_string("print_host").empty());
|
g_buttons[abSendGCode]->Show(show && !tab->m_config->opt_string("print_host").empty());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_info_sizer(bool show)
|
void show_info_sizer(const bool show)
|
||||||
{
|
{
|
||||||
g_scrolled_window_sizer->Show(static_cast<size_t>(0), show);
|
g_info_sizer->Show(static_cast<size_t>(0), show);
|
||||||
g_scrolled_window_sizer->Show(1, show && g_show_print_info);
|
g_info_sizer->Show(1, show && g_show_print_info);
|
||||||
g_manifold_warning_icon->Show(show && g_show_manifold_warning_icon);
|
g_manifold_warning_icon->Show(show && g_show_manifold_warning_icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1162,31 +1173,22 @@ void show_object_name(bool show)
|
|||||||
|
|
||||||
void update_mode()
|
void update_mode()
|
||||||
{
|
{
|
||||||
wxWindowUpdateLocker noUpdates(g_right_panel);
|
wxWindowUpdateLocker noUpdates(g_right_panel->GetParent());
|
||||||
|
|
||||||
// TODO There is a not the best place of it!
|
|
||||||
//*** Update style of the "Export G-code" button****
|
|
||||||
if (g_btn_export_gcode->GetFont() != bold_font()){
|
|
||||||
g_btn_export_gcode->SetBackgroundColour(wxColour(252, 77, 1));
|
|
||||||
g_btn_export_gcode->SetFont(bold_font());
|
|
||||||
}
|
|
||||||
// ***********************************
|
|
||||||
|
|
||||||
ConfigMenuIDs mode = get_view_mode();
|
ConfigMenuIDs mode = get_view_mode();
|
||||||
|
|
||||||
// show_frequently_changed_parameters(mode >= ConfigMenuModeRegular);
|
|
||||||
// g_expert_mode_part_sizer->Show(mode == ConfigMenuModeExpert);
|
|
||||||
g_object_list_sizer->Show(mode == ConfigMenuModeExpert);
|
g_object_list_sizer->Show(mode == ConfigMenuModeExpert);
|
||||||
show_info_sizer(mode == ConfigMenuModeExpert);
|
show_info_sizer(mode == ConfigMenuModeExpert);
|
||||||
show_buttons(mode == ConfigMenuModeExpert);
|
show_buttons(mode == ConfigMenuModeExpert);
|
||||||
show_object_name(mode == ConfigMenuModeSimple);
|
show_object_name(mode == ConfigMenuModeSimple);
|
||||||
|
show_manipulation_sizer(mode == ConfigMenuModeSimple);
|
||||||
|
|
||||||
// TODO There is a not the best place of it!
|
// TODO There is a not the best place of it!
|
||||||
// *** Update showing of the collpane_settings
|
// *** Update showing of the collpane_settings
|
||||||
// show_collpane_settings(mode == ConfigMenuModeExpert);
|
// show_collpane_settings(mode == ConfigMenuModeExpert);
|
||||||
// *************************
|
// *************************
|
||||||
g_right_panel->Layout();
|
g_right_panel->Layout();
|
||||||
g_right_panel->GetParent()->GetParent()->Layout();
|
g_right_panel->GetParent()->Layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_expert_mode(){
|
bool is_expert_mode(){
|
||||||
@ -1254,12 +1256,77 @@ int get_export_option(wxFileDialog* dlg)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_current_screen_size(unsigned &width, unsigned &height)
|
bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height)
|
||||||
{
|
{
|
||||||
wxDisplay display(wxDisplay::GetFromWindow(g_wxMainFrame));
|
const auto idx = wxDisplay::GetFromWindow(window);
|
||||||
|
if (idx == wxNOT_FOUND) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxDisplay display(idx);
|
||||||
const auto disp_size = display.GetClientArea();
|
const auto disp_size = display.GetClientArea();
|
||||||
width = disp_size.GetWidth();
|
width = disp_size.GetWidth();
|
||||||
height = disp_size.GetHeight();
|
height = disp_size.GetHeight();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void save_window_size(wxTopLevelWindow *window, const std::string &name)
|
||||||
|
{
|
||||||
|
const wxSize size = window->GetSize();
|
||||||
|
const wxPoint pos = window->GetPosition();
|
||||||
|
const auto maximized = window->IsMaximized() ? "1" : "0";
|
||||||
|
|
||||||
|
g_AppConfig->set((boost::format("window_%1%_size") % name).str(), (boost::format("%1%;%2%") % size.GetWidth() % size.GetHeight()).str());
|
||||||
|
g_AppConfig->set((boost::format("window_%1%_maximized") % name).str(), maximized);
|
||||||
|
}
|
||||||
|
|
||||||
|
void restore_window_size(wxTopLevelWindow *window, const std::string &name)
|
||||||
|
{
|
||||||
|
// XXX: This still doesn't behave nicely in some situations (mostly on Linux).
|
||||||
|
// The problem is that it's hard to obtain window position with respect to screen geometry reliably
|
||||||
|
// from wxWidgets. Sometimes wxWidgets claim a window is located on a different screen than on which
|
||||||
|
// it's actually visible. I suspect this has something to do with window initialization (maybe we
|
||||||
|
// restore window geometry too early), but haven't yet found a workaround.
|
||||||
|
|
||||||
|
const auto display_idx = wxDisplay::GetFromWindow(window);
|
||||||
|
if (display_idx == wxNOT_FOUND) { return; }
|
||||||
|
|
||||||
|
const auto display = wxDisplay(display_idx).GetClientArea();
|
||||||
|
std::vector<std::string> pair;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const auto key_size = (boost::format("window_%1%_size") % name).str();
|
||||||
|
if (g_AppConfig->has(key_size)) {
|
||||||
|
if (unescape_strings_cstyle(g_AppConfig->get(key_size), pair) && pair.size() == 2) {
|
||||||
|
auto width = boost::lexical_cast<int>(pair[0]);
|
||||||
|
auto height = boost::lexical_cast<int>(pair[1]);
|
||||||
|
|
||||||
|
window->SetSize(width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(const boost::bad_lexical_cast &) {}
|
||||||
|
|
||||||
|
// Maximizing should be the last thing to do.
|
||||||
|
// This ensure the size and position are sane when the user un-maximizes the window.
|
||||||
|
const auto key_maximized = (boost::format("window_%1%_maximized") % name).str();
|
||||||
|
if (g_AppConfig->get(key_maximized) == "1") {
|
||||||
|
window->Maximize(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable_action_buttons(bool enable)
|
||||||
|
{
|
||||||
|
if (g_buttons.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Update background colour for buttons
|
||||||
|
const wxColour bgrd_color = enable ? wxColour(224, 224, 224/*255, 96, 0*/) : wxColour(204, 204, 204);
|
||||||
|
|
||||||
|
for (auto btn : g_buttons) {
|
||||||
|
btn->Enable(enable);
|
||||||
|
btn->SetBackgroundColour(bgrd_color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void about()
|
void about()
|
||||||
|
@ -27,6 +27,7 @@ class wxButton;
|
|||||||
class wxFileDialog;
|
class wxFileDialog;
|
||||||
class wxStaticBitmap;
|
class wxStaticBitmap;
|
||||||
class wxFont;
|
class wxFont;
|
||||||
|
class wxTopLevelWindow;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -38,7 +39,6 @@ class AppConfig;
|
|||||||
class PresetUpdater;
|
class PresetUpdater;
|
||||||
class DynamicPrintConfig;
|
class DynamicPrintConfig;
|
||||||
class TabIface;
|
class TabIface;
|
||||||
class _3DScene;
|
|
||||||
|
|
||||||
#define _(s) Slic3r::GUI::I18N::translate((s))
|
#define _(s) Slic3r::GUI::I18N::translate((s))
|
||||||
|
|
||||||
@ -106,13 +106,10 @@ void set_plater(wxPanel *plater);
|
|||||||
void set_app_config(AppConfig *app_config);
|
void set_app_config(AppConfig *app_config);
|
||||||
void set_preset_bundle(PresetBundle *preset_bundle);
|
void set_preset_bundle(PresetBundle *preset_bundle);
|
||||||
void set_preset_updater(PresetUpdater *updater);
|
void set_preset_updater(PresetUpdater *updater);
|
||||||
void set_3DScene(_3DScene *scene);
|
|
||||||
void set_objects_from_perl( wxWindow* parent,
|
void set_objects_from_perl( wxWindow* parent,
|
||||||
wxBoxSizer *frequently_changed_parameters_sizer,
|
wxBoxSizer *frequently_changed_parameters_sizer,
|
||||||
wxBoxSizer *expert_mode_part_sizer,
|
wxBoxSizer *info_sizer,
|
||||||
wxBoxSizer *scrolled_window_sizer,
|
|
||||||
wxButton *btn_export_gcode,
|
wxButton *btn_export_gcode,
|
||||||
wxButton *btn_export_stl,
|
|
||||||
wxButton *btn_reslice,
|
wxButton *btn_reslice,
|
||||||
wxButton *btn_print,
|
wxButton *btn_print,
|
||||||
wxButton *btn_send_gcode,
|
wxButton *btn_send_gcode,
|
||||||
@ -196,6 +193,8 @@ bool select_language(wxArrayString & names, wxArrayLong & identifiers);
|
|||||||
// update right panel of the Plater according to view mode
|
// update right panel of the Plater according to view mode
|
||||||
void update_mode();
|
void update_mode();
|
||||||
|
|
||||||
|
void show_info_sizer(const bool show);
|
||||||
|
|
||||||
std::vector<Tab *>& get_tabs_list();
|
std::vector<Tab *>& get_tabs_list();
|
||||||
bool checked_tab(Tab* tab);
|
bool checked_tab(Tab* tab);
|
||||||
void delete_tab_from_list(Tab* tab);
|
void delete_tab_from_list(Tab* tab);
|
||||||
@ -214,12 +213,11 @@ wxString L_str(const std::string &str);
|
|||||||
// Return wxString from std::string in UTF8
|
// Return wxString from std::string in UTF8
|
||||||
wxString from_u8(const std::string &str);
|
wxString from_u8(const std::string &str);
|
||||||
|
|
||||||
void add_expert_mode_part( wxWindow* parent, wxBoxSizer* sizer,
|
void set_model_events_from_perl(Model &model,
|
||||||
Model &model,
|
int event_object_selection_changed,
|
||||||
int event_object_selection_changed,
|
int event_object_settings_changed,
|
||||||
int event_object_settings_changed,
|
int event_remove_object,
|
||||||
int event_remove_object,
|
int event_update_scene);
|
||||||
int event_update_scene);
|
|
||||||
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
|
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
|
||||||
// Update view mode according to selected menu
|
// Update view mode according to selected menu
|
||||||
void update_mode();
|
void update_mode();
|
||||||
@ -236,7 +234,15 @@ void add_export_option(wxFileDialog* dlg, const std::string& format);
|
|||||||
int get_export_option(wxFileDialog* dlg);
|
int get_export_option(wxFileDialog* dlg);
|
||||||
|
|
||||||
// Returns the dimensions of the screen on which the main frame is displayed
|
// Returns the dimensions of the screen on which the main frame is displayed
|
||||||
void get_current_screen_size(unsigned &width, unsigned &height);
|
bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height);
|
||||||
|
|
||||||
|
// Save window size and maximized status into AppConfig
|
||||||
|
void save_window_size(wxTopLevelWindow *window, const std::string &name);
|
||||||
|
// Restore the above
|
||||||
|
void restore_window_size(wxTopLevelWindow *window, const std::string &name);
|
||||||
|
|
||||||
|
// Update buttons view according to enable/disable
|
||||||
|
void enable_action_buttons(bool enable);
|
||||||
|
|
||||||
// Display an About dialog
|
// Display an About dialog
|
||||||
extern void about();
|
extern void about();
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
#include "Geometry.hpp"
|
#include "Geometry.hpp"
|
||||||
#include "slic3r/Utils/FixModelByWin10.hpp"
|
#include "slic3r/Utils/FixModelByWin10.hpp"
|
||||||
|
|
||||||
|
#include <wx/glcanvas.h>
|
||||||
|
#include "3DScene.hpp"
|
||||||
|
|
||||||
namespace Slic3r
|
namespace Slic3r
|
||||||
{
|
{
|
||||||
namespace GUI
|
namespace GUI
|
||||||
@ -23,10 +26,12 @@ wxSizer *m_sizer_object_movers = nullptr;
|
|||||||
wxDataViewCtrl *m_objects_ctrl = nullptr;
|
wxDataViewCtrl *m_objects_ctrl = nullptr;
|
||||||
PrusaObjectDataViewModel *m_objects_model = nullptr;
|
PrusaObjectDataViewModel *m_objects_model = nullptr;
|
||||||
wxCollapsiblePane *m_collpane_settings = nullptr;
|
wxCollapsiblePane *m_collpane_settings = nullptr;
|
||||||
|
PrusaDoubleSlider *m_slider = nullptr;
|
||||||
|
wxGLCanvas *m_preview_canvas = nullptr;
|
||||||
|
|
||||||
wxIcon m_icon_modifiermesh;
|
wxBitmap m_icon_modifiermesh;
|
||||||
wxIcon m_icon_solidmesh;
|
wxBitmap m_icon_solidmesh;
|
||||||
wxIcon m_icon_manifold_warning;
|
wxBitmap m_icon_manifold_warning;
|
||||||
wxBitmap m_bmp_cog;
|
wxBitmap m_bmp_cog;
|
||||||
wxBitmap m_bmp_split;
|
wxBitmap m_bmp_split;
|
||||||
|
|
||||||
@ -64,8 +69,6 @@ bool m_part_settings_changed = false;
|
|||||||
wxString g_selected_extruder = "";
|
wxString g_selected_extruder = "";
|
||||||
#endif //__WXOSX__
|
#endif //__WXOSX__
|
||||||
|
|
||||||
// typedef std::map<std::string, std::string> t_category_icon;
|
|
||||||
typedef std::map<std::string, wxBitmap> t_category_icon;
|
|
||||||
inline t_category_icon& get_category_icon() {
|
inline t_category_icon& get_category_icon() {
|
||||||
static t_category_icon CATEGORY_ICON;
|
static t_category_icon CATEGORY_ICON;
|
||||||
if (CATEGORY_ICON.empty()){
|
if (CATEGORY_ICON.empty()){
|
||||||
@ -138,11 +141,11 @@ void set_objects_from_model(Model &model) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init_mesh_icons(){
|
void init_mesh_icons(){
|
||||||
m_icon_modifiermesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
|
m_icon_modifiermesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
|
||||||
m_icon_solidmesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
|
m_icon_solidmesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
|
||||||
|
|
||||||
// init icon for manifold warning
|
// init icon for manifold warning
|
||||||
m_icon_manifold_warning = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
|
m_icon_manifold_warning = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
|
||||||
|
|
||||||
// init bitmap for "Split to sub-objects" context menu
|
// init bitmap for "Split to sub-objects" context menu
|
||||||
m_bmp_split = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG);
|
m_bmp_split = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG);
|
||||||
@ -233,7 +236,7 @@ wxDataViewColumn* object_ctrl_create_extruder_column(int extruders_count)
|
|||||||
void create_objects_ctrl(wxWindow* win, wxBoxSizer*& objects_sz)
|
void create_objects_ctrl(wxWindow* win, wxBoxSizer*& objects_sz)
|
||||||
{
|
{
|
||||||
m_objects_ctrl = new wxDataViewCtrl(win, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
m_objects_ctrl = new wxDataViewCtrl(win, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||||
m_objects_ctrl->SetInitialSize(wxSize(-1, 150)); // TODO - Set correct height according to the opened/closed objects
|
m_objects_ctrl->SetMinSize(wxSize(-1, 150)); // TODO - Set correct height according to the opened/closed objects
|
||||||
|
|
||||||
objects_sz = new wxBoxSizer(wxVERTICAL);
|
objects_sz = new wxBoxSizer(wxVERTICAL);
|
||||||
objects_sz->Add(m_objects_ctrl, 1, wxGROW | wxLEFT, 20);
|
objects_sz->Add(m_objects_ctrl, 1, wxGROW | wxLEFT, 20);
|
||||||
@ -245,9 +248,10 @@ void create_objects_ctrl(wxWindow* win, wxBoxSizer*& objects_sz)
|
|||||||
m_objects_ctrl->EnableDropTarget(wxDF_UNICODETEXT);
|
m_objects_ctrl->EnableDropTarget(wxDF_UNICODETEXT);
|
||||||
#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
|
#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
|
||||||
|
|
||||||
// column 0(Icon+Text) of the view control:
|
// column 0(Icon+Text) of the view control:
|
||||||
m_objects_ctrl->AppendIconTextColumn(_(L("Name")), 0, wxDATAVIEW_CELL_INERT, 200,
|
// And Icon can be consisting of several bitmaps
|
||||||
wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE);
|
m_objects_ctrl->AppendColumn(new wxDataViewColumn(_(L("Name")), new PrusaBitmapTextRenderer(),
|
||||||
|
0, 200, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
|
||||||
|
|
||||||
// column 1 of the view control:
|
// column 1 of the view control:
|
||||||
m_objects_ctrl->AppendTextColumn(_(L("Copy")), 1, wxDATAVIEW_CELL_INERT, 45,
|
m_objects_ctrl->AppendTextColumn(_(L("Copy")), 1, wxDATAVIEW_CELL_INERT, 45,
|
||||||
@ -278,7 +282,7 @@ wxBoxSizer* create_objects_list(wxWindow *win)
|
|||||||
|
|
||||||
m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, [](wxDataViewEvent& event) {
|
m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, [](wxDataViewEvent& event) {
|
||||||
object_ctrl_context_menu();
|
object_ctrl_context_menu();
|
||||||
event.Skip();
|
// event.Skip();
|
||||||
});
|
});
|
||||||
|
|
||||||
m_objects_ctrl->Bind(wxEVT_CHAR, [](wxKeyEvent& event) { object_ctrl_key_event(event); }); // doesn't work on OSX
|
m_objects_ctrl->Bind(wxEVT_CHAR, [](wxKeyEvent& event) { object_ctrl_key_event(event); }); // doesn't work on OSX
|
||||||
@ -316,15 +320,15 @@ wxBoxSizer* create_edit_object_buttons(wxWindow* win)
|
|||||||
|
|
||||||
//*** button's functions
|
//*** button's functions
|
||||||
btn_load_part->Bind(wxEVT_BUTTON, [win](wxEvent&) {
|
btn_load_part->Bind(wxEVT_BUTTON, [win](wxEvent&) {
|
||||||
on_btn_load(win);
|
// on_btn_load(win);
|
||||||
});
|
});
|
||||||
|
|
||||||
btn_load_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) {
|
btn_load_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) {
|
||||||
on_btn_load(win, true);
|
// on_btn_load(win, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
btn_load_lambda_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) {
|
btn_load_lambda_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) {
|
||||||
on_btn_load(win, true, true);
|
// on_btn_load(win, true, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
btn_delete ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_del(); });
|
btn_delete ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_del(); });
|
||||||
@ -660,7 +664,7 @@ void add_object_to_list(const std::string &name, ModelObject* model_object)
|
|||||||
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
||||||
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
|
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
|
||||||
if (errors > 0) {
|
if (errors > 0) {
|
||||||
const wxDataViewIconText data(item_name, m_icon_manifold_warning);
|
const PrusaDataViewBitmapText data(item_name, m_icon_manifold_warning);
|
||||||
wxVariant variant;
|
wxVariant variant;
|
||||||
variant << data;
|
variant << data;
|
||||||
m_objects_model->SetValue(variant, item, 0);
|
m_objects_model->SetValue(variant, item, 0);
|
||||||
@ -724,15 +728,26 @@ void select_current_object(int idx)
|
|||||||
{
|
{
|
||||||
g_prevent_list_events = true;
|
g_prevent_list_events = true;
|
||||||
m_objects_ctrl->UnselectAll();
|
m_objects_ctrl->UnselectAll();
|
||||||
if (idx < 0) {
|
if (idx>=0)
|
||||||
g_prevent_list_events = false;
|
m_objects_ctrl->Select(m_objects_model->GetItemById(idx));
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_objects_ctrl->Select(m_objects_model->GetItemById(idx));
|
|
||||||
part_selection_changed();
|
part_selection_changed();
|
||||||
g_prevent_list_events = false;
|
g_prevent_list_events = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void select_current_volume(int idx, int vol_idx)
|
||||||
|
{
|
||||||
|
if (vol_idx < 0) {
|
||||||
|
select_current_object(idx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_prevent_list_events = true;
|
||||||
|
m_objects_ctrl->UnselectAll();
|
||||||
|
if (idx >= 0)
|
||||||
|
m_objects_ctrl->Select(m_objects_model->GetItemByVolumeId(idx, vol_idx));
|
||||||
|
part_selection_changed();
|
||||||
|
g_prevent_list_events = false;
|
||||||
|
}
|
||||||
|
|
||||||
void remove()
|
void remove()
|
||||||
{
|
{
|
||||||
auto item = m_objects_ctrl->GetSelection();
|
auto item = m_objects_ctrl->GetSelection();
|
||||||
@ -758,8 +773,17 @@ void object_ctrl_selection_changed()
|
|||||||
|
|
||||||
if (m_event_object_selection_changed > 0) {
|
if (m_event_object_selection_changed > 0) {
|
||||||
wxCommandEvent event(m_event_object_selection_changed);
|
wxCommandEvent event(m_event_object_selection_changed);
|
||||||
event.SetInt(int(m_objects_model->GetParent(m_objects_ctrl->GetSelection()) != wxDataViewItem(0)));
|
event.SetId(m_selected_object_id); // set $obj_idx
|
||||||
event.SetId(m_selected_object_id);
|
const wxDataViewItem item = m_objects_ctrl->GetSelection();
|
||||||
|
if (!item || m_objects_model->GetParent(item) == wxDataViewItem(0))
|
||||||
|
event.SetInt(-1); // set $vol_idx
|
||||||
|
else {
|
||||||
|
const int vol_idx = m_objects_model->GetVolumeIdByItem(item);
|
||||||
|
if (vol_idx == -2) // is settings item
|
||||||
|
event.SetInt(m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item))); // set $vol_idx
|
||||||
|
else
|
||||||
|
event.SetInt(vol_idx);
|
||||||
|
}
|
||||||
get_main_frame()->ProcessWindowEvent(event);
|
get_main_frame()->ProcessWindowEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,9 +796,9 @@ void object_ctrl_context_menu()
|
|||||||
{
|
{
|
||||||
wxDataViewItem item;
|
wxDataViewItem item;
|
||||||
wxDataViewColumn* col;
|
wxDataViewColumn* col;
|
||||||
printf("object_ctrl_context_menu\n");
|
// printf("object_ctrl_context_menu\n");
|
||||||
const wxPoint pt = get_mouse_position_in_control();
|
const wxPoint pt = get_mouse_position_in_control();
|
||||||
printf("mouse_position_in_control: x = %d, y = %d\n", pt.x, pt.y);
|
// printf("mouse_position_in_control: x = %d, y = %d\n", pt.x, pt.y);
|
||||||
m_objects_ctrl->HitTest(pt, item, col);
|
m_objects_ctrl->HitTest(pt, item, col);
|
||||||
if (!item)
|
if (!item)
|
||||||
#ifdef __WXOSX__ // #ys_FIXME temporary workaround for OSX
|
#ifdef __WXOSX__ // #ys_FIXME temporary workaround for OSX
|
||||||
@ -788,9 +812,9 @@ void object_ctrl_context_menu()
|
|||||||
#else
|
#else
|
||||||
return;
|
return;
|
||||||
#endif // __WXOSX__
|
#endif // __WXOSX__
|
||||||
printf("item exists\n");
|
// printf("item exists\n");
|
||||||
const wxString title = col->GetTitle();
|
const wxString title = col->GetTitle();
|
||||||
printf("title = *%s*\n", title.data().AsChar());
|
// printf("title = *%s*\n", title.data().AsChar());
|
||||||
|
|
||||||
if (title == " ")
|
if (title == " ")
|
||||||
show_context_menu();
|
show_context_menu();
|
||||||
@ -836,6 +860,15 @@ void object_ctrl_item_value_change(wxDataViewEvent& event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void show_manipulation_og(const bool show)
|
||||||
|
{
|
||||||
|
wxGridSizer* grid_sizer = get_optgroup(ogFrequentlyObjectSettings)->get_grid_sizer();
|
||||||
|
if (show == grid_sizer->IsShown(2))
|
||||||
|
return;
|
||||||
|
for (size_t id = 2; id < 12; id++)
|
||||||
|
grid_sizer->Show(id, show);
|
||||||
|
}
|
||||||
|
|
||||||
//update_optgroup
|
//update_optgroup
|
||||||
void update_settings_list()
|
void update_settings_list()
|
||||||
{
|
{
|
||||||
@ -856,14 +889,19 @@ void update_settings_list()
|
|||||||
|
|
||||||
m_option_sizer->Clear(true);
|
m_option_sizer->Clear(true);
|
||||||
|
|
||||||
if (m_config)
|
bool show_manipulations = true;
|
||||||
|
const auto item = m_objects_ctrl->GetSelection();
|
||||||
|
if (m_config && m_objects_model->IsSettingsItem(item))
|
||||||
{
|
{
|
||||||
auto extra_column = [](wxWindow* parent, const Line& line)
|
auto extra_column = [](wxWindow* parent, const Line& line)
|
||||||
{
|
{
|
||||||
auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line
|
auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line
|
||||||
|
|
||||||
auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("erase.png")), wxBITMAP_TYPE_PNG),
|
auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(from_u8(var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG),
|
||||||
wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||||
|
#ifdef __WXMSW__
|
||||||
|
btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||||
|
#endif // __WXMSW__
|
||||||
btn->Bind(wxEVT_BUTTON, [opt_key](wxEvent &event){
|
btn->Bind(wxEVT_BUTTON, [opt_key](wxEvent &event){
|
||||||
(*m_config)->erase(opt_key);
|
(*m_config)->erase(opt_key);
|
||||||
wxTheApp->CallAfter([]() { update_settings_list(); });
|
wxTheApp->CallAfter([]() { update_settings_list(); });
|
||||||
@ -873,60 +911,75 @@ void update_settings_list()
|
|||||||
|
|
||||||
std::map<std::string, std::vector<std::string>> cat_options;
|
std::map<std::string, std::vector<std::string>> cat_options;
|
||||||
auto opt_keys = (*m_config)->keys();
|
auto opt_keys = (*m_config)->keys();
|
||||||
if (opt_keys.size() == 1 && opt_keys[0] == "extruder")
|
m_og_settings.resize(0);
|
||||||
return;
|
std::vector<std::string> categories;
|
||||||
|
if (!(opt_keys.size() == 1 && opt_keys[0] == "extruder"))// return;
|
||||||
|
{
|
||||||
|
auto extruders_cnt = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
|
||||||
|
get_preset_bundle()->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||||
|
|
||||||
auto extruders_cnt = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
|
for (auto& opt_key : opt_keys) {
|
||||||
get_preset_bundle()->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
auto category = (*m_config)->def()->get(opt_key)->category;
|
||||||
|
if (category.empty() ||
|
||||||
|
(category == "Extruders" && extruders_cnt == 1)) continue;
|
||||||
|
|
||||||
for (auto& opt_key : opt_keys) {
|
std::vector< std::string > new_category;
|
||||||
auto category = (*m_config)->def()->get(opt_key)->category;
|
|
||||||
if (category.empty() ||
|
|
||||||
(category == "Extruders" && extruders_cnt==1)) continue;
|
|
||||||
|
|
||||||
std::vector< std::string > new_category;
|
auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category);
|
||||||
|
cat_opt.push_back(opt_key);
|
||||||
|
if (cat_opt.size() == 1)
|
||||||
|
cat_options[category] = cat_opt;
|
||||||
|
}
|
||||||
|
|
||||||
auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category);
|
for (auto& cat : cat_options) {
|
||||||
cat_opt.push_back(opt_key);
|
if (cat.second.size() == 1 && cat.second[0] == "extruder")
|
||||||
if (cat_opt.size() == 1)
|
continue;
|
||||||
cat_options[category] = cat_opt;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
auto optgroup = std::make_shared<ConfigOptionsGroup>(parent, cat.first, *m_config, false, ogDEFAULT, extra_column);
|
||||||
|
optgroup->label_width = 150;
|
||||||
|
optgroup->sidetext_width = 70;
|
||||||
|
|
||||||
m_og_settings.resize(0);
|
for (auto& opt : cat.second)
|
||||||
for (auto& cat : cat_options) {
|
{
|
||||||
if (cat.second.size() == 1 && cat.second[0] == "extruder")
|
if (opt == "extruder")
|
||||||
continue;
|
continue;
|
||||||
|
Option option = optgroup->get_option(opt);
|
||||||
|
option.opt.width = 70;
|
||||||
|
optgroup->append_single_option_line(option);
|
||||||
|
}
|
||||||
|
optgroup->reload_config();
|
||||||
|
m_option_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
|
||||||
|
m_og_settings.push_back(optgroup);
|
||||||
|
|
||||||
auto optgroup = std::make_shared<ConfigOptionsGroup>(parent, cat.first, *m_config, false, ogDEFAULT, extra_column);
|
categories.push_back(cat.first);
|
||||||
optgroup->label_width = 100;
|
}
|
||||||
optgroup->sidetext_width = 70;
|
}
|
||||||
|
|
||||||
for (auto& opt : cat.second)
|
if (m_og_settings.empty()) {
|
||||||
{
|
m_objects_ctrl->Select(m_objects_model->Delete(item));
|
||||||
if (opt == "extruder")
|
part_selection_changed();
|
||||||
continue;
|
}
|
||||||
Option option = optgroup->get_option(opt);
|
else {
|
||||||
option.opt.width = 70;
|
if (!categories.empty())
|
||||||
optgroup->append_single_option_line(option);
|
m_objects_model->UpdateSettingsDigest(item, categories);
|
||||||
}
|
show_manipulations = false;
|
||||||
optgroup->reload_config();
|
}
|
||||||
m_option_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
|
|
||||||
m_og_settings.push_back(optgroup);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
show_manipulation_og(show_manipulations);
|
||||||
|
show_info_sizer(show_manipulations && item && m_objects_model->GetParent(item) == wxDataViewItem(0));
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
no_updates.reset(nullptr);
|
no_updates.reset(nullptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*get_right_panel()*/parent->Layout();
|
parent->Layout();
|
||||||
get_right_panel()->GetParent()->GetParent()->Layout();
|
get_right_panel()->GetParent()->Layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_settings_choice(wxMenu *menu, int id, bool is_part)
|
void get_settings_choice(wxMenu *menu, int id, bool is_part)
|
||||||
{
|
{
|
||||||
auto category_name = menu->GetLabel(id);
|
const auto category_name = menu->GetLabel(id);
|
||||||
|
|
||||||
wxArrayString names;
|
wxArrayString names;
|
||||||
wxArrayInt selections;
|
wxArrayInt selections;
|
||||||
@ -974,15 +1027,35 @@ void get_settings_choice(wxMenu *menu, int id, bool is_part)
|
|||||||
(*m_config)->set_key_value(opt_key, m_default_config.get()->option(opt_key)->clone());
|
(*m_config)->set_key_value(opt_key, m_default_config.get()->option(opt_key)->clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Add settings item for object
|
||||||
|
const auto item = m_objects_ctrl->GetSelection();
|
||||||
|
if (item) {
|
||||||
|
const auto settings_item = m_objects_model->HasSettings(item);
|
||||||
|
m_objects_ctrl->Select(settings_item ? settings_item :
|
||||||
|
m_objects_model->AddSettingsChild(item));
|
||||||
|
#ifndef __WXOSX__
|
||||||
|
part_selection_changed();
|
||||||
|
#endif //no __WXOSX__
|
||||||
|
}
|
||||||
|
else
|
||||||
update_settings_list();
|
update_settings_list();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cur_item_hase_children()
|
void menu_item_add_generic(wxMenuItem* &menu, int id) {
|
||||||
{
|
auto sub_menu = new wxMenu;
|
||||||
wxDataViewItemArray children;
|
|
||||||
if (m_objects_model->GetChildren(m_objects_ctrl->GetSelection(), children) > 0)
|
std::vector<std::string> menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") };
|
||||||
return true;
|
for (auto& item : menu_items)
|
||||||
return false;
|
sub_menu->Append(new wxMenuItem(sub_menu, ++id, _(item)));
|
||||||
|
|
||||||
|
#ifndef __WXMSW__
|
||||||
|
sub_menu->Bind(wxEVT_MENU, [sub_menu](wxEvent &event) {
|
||||||
|
load_lambda(sub_menu->GetLabel(event.GetId()).ToStdString());
|
||||||
|
});
|
||||||
|
#endif //no __WXMSW__
|
||||||
|
|
||||||
|
menu->SetSubMenu(sub_menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
wxMenuItem* menu_item_split(wxMenu* menu, int id) {
|
wxMenuItem* menu_item_split(wxMenu* menu, int id) {
|
||||||
@ -991,11 +1064,11 @@ wxMenuItem* menu_item_split(wxMenu* menu, int id) {
|
|||||||
return menu_item;
|
return menu_item;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxMenuItem* menu_item_settings(wxMenu* menu, int id) {
|
wxMenuItem* menu_item_settings(wxMenu* menu, int id, const bool is_part) {
|
||||||
auto menu_item = new wxMenuItem(menu, id, _(L("Add settings")));
|
auto menu_item = new wxMenuItem(menu, id, _(L("Add settings")));
|
||||||
menu_item->SetBitmap(m_bmp_cog);
|
menu_item->SetBitmap(m_bmp_cog);
|
||||||
|
|
||||||
auto sub_menu = create_add_settings_popupmenu(false);
|
auto sub_menu = create_add_settings_popupmenu(is_part);
|
||||||
menu_item->SetSubMenu(sub_menu);
|
menu_item->SetSubMenu(sub_menu);
|
||||||
return menu_item;
|
return menu_item;
|
||||||
}
|
}
|
||||||
@ -1005,44 +1078,54 @@ wxMenu *create_add_part_popupmenu()
|
|||||||
wxMenu *menu = new wxMenu;
|
wxMenu *menu = new wxMenu;
|
||||||
std::vector<std::string> menu_items = { L("Add part"), L("Add modifier"), L("Add generic") };
|
std::vector<std::string> menu_items = { L("Add part"), L("Add modifier"), L("Add generic") };
|
||||||
|
|
||||||
wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size()+2);
|
wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size()+4+2);
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (auto& item : menu_items) {
|
for (auto& item : menu_items) {
|
||||||
auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item));
|
auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item));
|
||||||
menu_item->SetBitmap(i == 0 ? m_icon_solidmesh : m_icon_modifiermesh);
|
menu_item->SetBitmap(i == 0 ? m_icon_solidmesh : m_icon_modifiermesh);
|
||||||
menu->Append(menu_item);
|
if (item == "Add generic")
|
||||||
|
menu_item_add_generic(menu_item, config_id_base + i);
|
||||||
|
menu->Append(menu_item);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
auto menu_item = menu_item_split(menu, config_id_base + i);
|
auto menu_item = menu_item_split(menu, config_id_base + i + 4);
|
||||||
menu->Append(menu_item);
|
menu->Append(menu_item);
|
||||||
menu_item->Enable(!cur_item_hase_children());
|
menu_item->Enable(is_splittable_object(false));
|
||||||
|
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
// Append settings popupmenu
|
// Append settings popupmenu
|
||||||
menu->Append(menu_item_settings(menu, config_id_base + i + 1));
|
menu->Append(menu_item_settings(menu, config_id_base + i + 5, false));
|
||||||
|
|
||||||
wxWindow* win = get_tab_panel()->GetPage(0);
|
menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){
|
||||||
|
|
||||||
menu->Bind(wxEVT_MENU, [config_id_base, win, menu](wxEvent &event){
|
|
||||||
switch (event.GetId() - config_id_base) {
|
switch (event.GetId() - config_id_base) {
|
||||||
case 0:
|
case 0:
|
||||||
on_btn_load(win);
|
on_btn_load();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
on_btn_load(win, true);
|
on_btn_load(true);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
on_btn_load(win, true, true);
|
// on_btn_load(true, true);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
case 6:
|
||||||
|
#ifdef __WXMSW__
|
||||||
|
load_lambda(menu->GetLabel(event.GetId()).ToStdString());
|
||||||
|
#endif // __WXMSW__
|
||||||
|
break;
|
||||||
|
case 7: //3:
|
||||||
on_btn_split(false);
|
on_btn_split(false);
|
||||||
break;
|
break;
|
||||||
default:{
|
default:
|
||||||
|
#ifdef __WXMSW__
|
||||||
get_settings_choice(menu, event.GetId(), false);
|
get_settings_choice(menu, event.GetId(), false);
|
||||||
break;}
|
#endif // __WXMSW__
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1054,11 +1137,13 @@ wxMenu *create_part_settings_popupmenu()
|
|||||||
wxMenu *menu = new wxMenu;
|
wxMenu *menu = new wxMenu;
|
||||||
wxWindowID config_id_base = wxWindow::NewControlId(2);
|
wxWindowID config_id_base = wxWindow::NewControlId(2);
|
||||||
|
|
||||||
menu->Append(menu_item_split(menu, config_id_base));
|
auto menu_item = menu_item_split(menu, config_id_base);
|
||||||
|
menu->Append(menu_item);
|
||||||
|
menu_item->Enable(is_splittable_object(true));
|
||||||
|
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
// Append settings popupmenu
|
// Append settings popupmenu
|
||||||
menu->Append(menu_item_settings(menu, config_id_base + 1));
|
menu->Append(menu_item_settings(menu, config_id_base + 1, true));
|
||||||
|
|
||||||
menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){
|
menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){
|
||||||
switch (event.GetId() - config_id_base) {
|
switch (event.GetId() - config_id_base) {
|
||||||
@ -1090,35 +1175,35 @@ wxMenu *create_add_settings_popupmenu(bool is_part)
|
|||||||
wxNullBitmap : categories.at(cat.first));
|
wxNullBitmap : categories.at(cat.first));
|
||||||
menu->Append(menu_item);
|
menu->Append(menu_item);
|
||||||
}
|
}
|
||||||
|
#ifndef __WXMSW__
|
||||||
menu->Bind(wxEVT_MENU, [menu](wxEvent &event) {
|
menu->Bind(wxEVT_MENU, [menu,is_part](wxEvent &event) {
|
||||||
get_settings_choice(menu, event.GetId(), true);
|
get_settings_choice(menu, event.GetId(), is_part);
|
||||||
});
|
});
|
||||||
|
#endif //no __WXMSW__
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_context_menu()
|
void show_context_menu()
|
||||||
{
|
{
|
||||||
auto item = m_objects_ctrl->GetSelection();
|
const auto item = m_objects_ctrl->GetSelection();
|
||||||
if (item)
|
if (item)
|
||||||
{
|
{
|
||||||
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
|
if (m_objects_model->IsSettingsItem(item))
|
||||||
auto menu = create_add_part_popupmenu();
|
return;
|
||||||
get_tab_panel()->GetPage(0)->PopupMenu(menu);
|
const auto menu = m_objects_model->GetParent(item) == wxDataViewItem(0) ?
|
||||||
}
|
create_add_part_popupmenu() :
|
||||||
else {
|
create_part_settings_popupmenu();
|
||||||
auto menu = create_part_settings_popupmenu();
|
get_tab_panel()->GetPage(0)->PopupMenu(menu);
|
||||||
get_tab_panel()->GetPage(0)->PopupMenu(menu);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ******
|
// ******
|
||||||
|
|
||||||
void load_part( wxWindow* parent, ModelObject* model_object,
|
void load_part( ModelObject* model_object,
|
||||||
wxArrayString& part_names, const bool is_modifier)
|
wxArrayString& part_names, const bool is_modifier)
|
||||||
{
|
{
|
||||||
|
wxWindow* parent = get_tab_panel()->GetPage(0);
|
||||||
|
|
||||||
wxArrayString input_files;
|
wxArrayString input_files;
|
||||||
open_model(parent, input_files);
|
open_model(parent, input_files);
|
||||||
for (int i = 0; i < input_files.size(); ++i) {
|
for (int i = 0; i < input_files.size(); ++i) {
|
||||||
@ -1137,7 +1222,7 @@ void load_part( wxWindow* parent, ModelObject* model_object,
|
|||||||
for ( auto object : model.objects) {
|
for ( auto object : model.objects) {
|
||||||
for (auto volume : object->volumes) {
|
for (auto volume : object->volumes) {
|
||||||
auto new_volume = model_object->add_volume(*volume);
|
auto new_volume = model_object->add_volume(*volume);
|
||||||
new_volume->modifier = is_modifier;
|
new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
|
||||||
boost::filesystem::path(input_file).filename().string();
|
boost::filesystem::path(input_file).filename().string();
|
||||||
new_volume->name = boost::filesystem::path(input_file).filename().string();
|
new_volume->name = boost::filesystem::path(input_file).filename().string();
|
||||||
|
|
||||||
@ -1156,10 +1241,10 @@ void load_part( wxWindow* parent, ModelObject* model_object,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_lambda( wxWindow* parent, ModelObject* model_object,
|
void load_lambda( ModelObject* model_object,
|
||||||
wxArrayString& part_names, const bool is_modifier)
|
wxArrayString& part_names, const bool is_modifier)
|
||||||
{
|
{
|
||||||
auto dlg = new LambdaObjectDialog(parent);
|
auto dlg = new LambdaObjectDialog(m_objects_ctrl->GetMainWindow());
|
||||||
if (dlg->ShowModal() == wxID_CANCEL) {
|
if (dlg->ShowModal() == wxID_CANCEL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1195,7 +1280,8 @@ void load_lambda( wxWindow* parent, ModelObject* model_object,
|
|||||||
mesh.repair();
|
mesh.repair();
|
||||||
|
|
||||||
auto new_volume = model_object->add_volume(mesh);
|
auto new_volume = model_object->add_volume(mesh);
|
||||||
new_volume->modifier = is_modifier;
|
new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
|
||||||
|
|
||||||
new_volume->name = name;
|
new_volume->name = name;
|
||||||
// set a default extruder value, since user can't add it manually
|
// set a default extruder value, since user can't add it manually
|
||||||
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||||
@ -1205,7 +1291,50 @@ void load_lambda( wxWindow* parent, ModelObject* model_object,
|
|||||||
m_parts_changed = true;
|
m_parts_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_btn_load(wxWindow* parent, bool is_modifier /*= false*/, bool is_lambda/* = false*/)
|
void load_lambda(const std::string& type_name)
|
||||||
|
{
|
||||||
|
if (m_selected_object_id < 0) return;
|
||||||
|
|
||||||
|
auto dlg = new LambdaObjectDialog(m_objects_ctrl->GetMainWindow(), type_name);
|
||||||
|
if (dlg->ShowModal() == wxID_CANCEL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::string name = "lambda-"+type_name;
|
||||||
|
TriangleMesh mesh;
|
||||||
|
|
||||||
|
const auto params = dlg->ObjectParameters();
|
||||||
|
if (type_name == _("Box"))
|
||||||
|
mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]);
|
||||||
|
else if (type_name == _("Cylinder"))
|
||||||
|
mesh = make_cylinder(params.cyl_r, params.cyl_h);
|
||||||
|
else if (type_name == _("Sphere"))
|
||||||
|
mesh = make_sphere(params.sph_rho);
|
||||||
|
else if (type_name == _("Slab")){
|
||||||
|
const auto& size = (*m_objects)[m_selected_object_id]->bounding_box().size();
|
||||||
|
mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h);
|
||||||
|
// box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
|
||||||
|
mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z);
|
||||||
|
}
|
||||||
|
mesh.repair();
|
||||||
|
|
||||||
|
auto new_volume = (*m_objects)[m_selected_object_id]->add_volume(mesh);
|
||||||
|
new_volume->set_type(ModelVolume::PARAMETER_MODIFIER);
|
||||||
|
|
||||||
|
new_volume->name = name;
|
||||||
|
// set a default extruder value, since user can't add it manually
|
||||||
|
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||||
|
|
||||||
|
m_parts_changed = true;
|
||||||
|
parts_changed(m_selected_object_id);
|
||||||
|
|
||||||
|
m_objects_ctrl->Select(m_objects_model->AddChild(m_objects_ctrl->GetSelection(),
|
||||||
|
name, m_icon_modifiermesh));
|
||||||
|
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||||
|
object_ctrl_selection_changed();
|
||||||
|
#endif //no __WXOSX__ //__WXMSW__
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_btn_load(bool is_modifier /*= false*/, bool is_lambda/* = false*/)
|
||||||
{
|
{
|
||||||
auto item = m_objects_ctrl->GetSelection();
|
auto item = m_objects_ctrl->GetSelection();
|
||||||
if (!item)
|
if (!item)
|
||||||
@ -1219,19 +1348,54 @@ void on_btn_load(wxWindow* parent, bool is_modifier /*= false*/, bool is_lambda/
|
|||||||
if (obj_idx < 0) return;
|
if (obj_idx < 0) return;
|
||||||
wxArrayString part_names;
|
wxArrayString part_names;
|
||||||
if (is_lambda)
|
if (is_lambda)
|
||||||
load_lambda(parent, (*m_objects)[obj_idx], part_names, is_modifier);
|
load_lambda((*m_objects)[obj_idx], part_names, is_modifier);
|
||||||
else
|
else
|
||||||
load_part(parent, (*m_objects)[obj_idx], part_names, is_modifier);
|
load_part((*m_objects)[obj_idx], part_names, is_modifier);
|
||||||
|
|
||||||
parts_changed(obj_idx);
|
parts_changed(obj_idx);
|
||||||
|
|
||||||
for (int i = 0; i < part_names.size(); ++i)
|
for (int i = 0; i < part_names.size(); ++i)
|
||||||
m_objects_ctrl->Select( m_objects_model->AddChild(item, part_names.Item(i),
|
m_objects_ctrl->Select( m_objects_model->AddChild(item, part_names.Item(i),
|
||||||
is_modifier ? m_icon_modifiermesh : m_icon_solidmesh));
|
is_modifier ? m_icon_modifiermesh : m_icon_solidmesh));
|
||||||
// part_selection_changed();
|
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||||
#ifdef __WXMSW__
|
|
||||||
object_ctrl_selection_changed();
|
object_ctrl_selection_changed();
|
||||||
#endif //__WXMSW__
|
#endif //no __WXOSX__//__WXMSW__
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_settings_from_config()
|
||||||
|
{
|
||||||
|
auto opt_keys = (*m_config)->keys();
|
||||||
|
if (opt_keys.size() == 1 && opt_keys[0] == "extruder")
|
||||||
|
return;
|
||||||
|
int extruder = -1;
|
||||||
|
if ((*m_config)->has("extruder"))
|
||||||
|
extruder = (*m_config)->option<ConfigOptionInt>("extruder")->value;
|
||||||
|
|
||||||
|
(*m_config)->clear();
|
||||||
|
|
||||||
|
if (extruder >=0 )
|
||||||
|
(*m_config)->set_key_value("extruder", new ConfigOptionInt(extruder));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remove_subobject_from_object(const int volume_id)
|
||||||
|
{
|
||||||
|
const auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
|
||||||
|
|
||||||
|
// if user is deleting the last solid part, throw error
|
||||||
|
int solid_cnt = 0;
|
||||||
|
for (auto vol : (*m_objects)[m_selected_object_id]->volumes)
|
||||||
|
if (vol->is_model_part())
|
||||||
|
++solid_cnt;
|
||||||
|
if (volume->is_model_part() && solid_cnt == 1) {
|
||||||
|
Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from this object.")));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*m_objects)[m_selected_object_id]->delete_volume(volume_id);
|
||||||
|
m_parts_changed = true;
|
||||||
|
|
||||||
|
parts_changed(m_selected_object_id);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_btn_del()
|
void on_btn_del()
|
||||||
@ -1239,50 +1403,67 @@ void on_btn_del()
|
|||||||
auto item = m_objects_ctrl->GetSelection();
|
auto item = m_objects_ctrl->GetSelection();
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
auto volume_id = m_objects_model->GetVolumeIdByItem(item);
|
const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||||
if (volume_id < 0)
|
if (volume_id ==-1)
|
||||||
return;
|
return;
|
||||||
auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
|
|
||||||
|
if (volume_id ==-2)
|
||||||
// if user is deleting the last solid part, throw error
|
remove_settings_from_config();
|
||||||
int solid_cnt = 0;
|
else if (!remove_subobject_from_object(volume_id))
|
||||||
for (auto vol : (*m_objects)[m_selected_object_id]->volumes)
|
return;
|
||||||
if (!vol->modifier)
|
|
||||||
++solid_cnt;
|
|
||||||
if (!volume->modifier && solid_cnt == 1) {
|
|
||||||
Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from this object.")));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*m_objects)[m_selected_object_id]->delete_volume(volume_id);
|
|
||||||
m_parts_changed = true;
|
|
||||||
|
|
||||||
parts_changed(m_selected_object_id);
|
|
||||||
|
|
||||||
m_objects_ctrl->Select(m_objects_model->Delete(item));
|
m_objects_ctrl->Select(m_objects_model->Delete(item));
|
||||||
part_selection_changed();
|
part_selection_changed();
|
||||||
// #ifdef __WXMSW__
|
}
|
||||||
// object_ctrl_selection_changed();
|
|
||||||
// #endif //__WXMSW__
|
bool get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume)
|
||||||
|
{
|
||||||
|
if (!item || m_selected_object_id < 0)
|
||||||
|
return false;
|
||||||
|
const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||||
|
if (volume_id < 0) {
|
||||||
|
if (split_part) return false;
|
||||||
|
volume = (*m_objects)[m_selected_object_id]->volumes[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
|
||||||
|
if (volume)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_splittable_object(const bool split_part)
|
||||||
|
{
|
||||||
|
const wxDataViewItem item = m_objects_ctrl->GetSelection();
|
||||||
|
if (!item) return false;
|
||||||
|
|
||||||
|
wxDataViewItemArray children;
|
||||||
|
if (!split_part && m_objects_model->GetChildren(item, children) > 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ModelVolume* volume;
|
||||||
|
if (!get_volume_by_item(split_part, item, volume) || !volume)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TriangleMeshPtrs meshptrs = volume->mesh.split();
|
||||||
|
if (meshptrs.size() <= 1) {
|
||||||
|
delete meshptrs.front();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_btn_split(const bool split_part)
|
void on_btn_split(const bool split_part)
|
||||||
{
|
{
|
||||||
auto item = m_objects_ctrl->GetSelection();
|
const auto item = m_objects_ctrl->GetSelection();
|
||||||
if (!item || m_selected_object_id<0)
|
if (!item || m_selected_object_id<0)
|
||||||
return;
|
return;
|
||||||
auto volume_id = m_objects_model->GetVolumeIdByItem(item);
|
|
||||||
ModelVolume* volume;
|
ModelVolume* volume;
|
||||||
if (volume_id < 0) {
|
if (!get_volume_by_item(split_part, item, volume)) return;
|
||||||
if (split_part) return;
|
DynamicPrintConfig& config = get_preset_bundle()->printers.get_edited_preset().config;
|
||||||
else
|
const auto nozzle_dmrs_cnt = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||||
volume = (*m_objects)[m_selected_object_id]->volumes[0]; }
|
if (volume->split(nozzle_dmrs_cnt) == 1) {
|
||||||
else
|
|
||||||
volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
|
|
||||||
DynamicPrintConfig& config = get_preset_bundle()->printers.get_edited_preset().config;
|
|
||||||
auto nozzle_dmrs_cnt = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
|
||||||
auto split_rez = volume->split(nozzle_dmrs_cnt);
|
|
||||||
if (split_rez == 1) {
|
|
||||||
wxMessageBox(_(L("The selected object couldn't be split because it contains only one part.")));
|
wxMessageBox(_(L("The selected object couldn't be split because it contains only one part.")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1295,8 +1476,9 @@ void on_btn_split(const bool split_part)
|
|||||||
|
|
||||||
for (auto id = 0; id < model_object->volumes.size(); id++)
|
for (auto id = 0; id < model_object->volumes.size(); id++)
|
||||||
m_objects_model->AddChild(parent, model_object->volumes[id]->name,
|
m_objects_model->AddChild(parent, model_object->volumes[id]->name,
|
||||||
model_object->volumes[id]->modifier ? m_icon_modifiermesh : m_icon_solidmesh,
|
model_object->volumes[id]->is_modifier() ? m_icon_modifiermesh : m_icon_solidmesh,
|
||||||
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
|
model_object->volumes[id]->config.has("extruder") ?
|
||||||
|
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
m_objects_ctrl->Expand(parent);
|
m_objects_ctrl->Expand(parent);
|
||||||
@ -1305,10 +1487,14 @@ void on_btn_split(const bool split_part)
|
|||||||
for (auto id = 0; id < model_object->volumes.size(); id++)
|
for (auto id = 0; id < model_object->volumes.size(); id++)
|
||||||
m_objects_model->AddChild(item, model_object->volumes[id]->name,
|
m_objects_model->AddChild(item, model_object->volumes[id]->name,
|
||||||
m_icon_solidmesh,
|
m_icon_solidmesh,
|
||||||
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
|
model_object->volumes[id]->config.has("extruder") ?
|
||||||
|
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
|
||||||
false);
|
false);
|
||||||
m_objects_ctrl->Expand(item);
|
m_objects_ctrl->Expand(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_parts_changed = true;
|
||||||
|
parts_changed(m_selected_object_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_btn_move_up(){
|
void on_btn_move_up(){
|
||||||
@ -1389,33 +1575,49 @@ void part_selection_changed()
|
|||||||
auto item = m_objects_ctrl->GetSelection();
|
auto item = m_objects_ctrl->GetSelection();
|
||||||
int obj_idx = -1;
|
int obj_idx = -1;
|
||||||
auto og = get_optgroup(ogFrequentlyObjectSettings);
|
auto og = get_optgroup(ogFrequentlyObjectSettings);
|
||||||
|
m_config = nullptr;
|
||||||
|
wxString object_name = wxEmptyString;
|
||||||
if (item)
|
if (item)
|
||||||
{
|
{
|
||||||
|
const bool is_settings_item = m_objects_model->IsSettingsItem(item);
|
||||||
bool is_part = false;
|
bool is_part = false;
|
||||||
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
|
wxString og_name = wxEmptyString;
|
||||||
|
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
|
||||||
obj_idx = m_objects_model->GetIdByItem(item);
|
obj_idx = m_objects_model->GetIdByItem(item);
|
||||||
og->set_name(" " + _(L("Object Settings")) + " ");
|
og_name = _(L("Object manipulation"));
|
||||||
m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->config);
|
m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->config);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto parent = m_objects_model->GetParent(item);
|
auto parent = m_objects_model->GetParent(item);
|
||||||
// Take ID of the parent object to "inform" perl-side which object have to be selected on the scene
|
// Take ID of the parent object to "inform" perl-side which object have to be selected on the scene
|
||||||
obj_idx = m_objects_model->GetIdByItem(parent);
|
obj_idx = m_objects_model->GetIdByItem(parent);
|
||||||
og->set_name(" " + _(L("Part Settings")) + " ");
|
if (is_settings_item) {
|
||||||
is_part = true;
|
if (m_objects_model->GetParent(parent) == wxDataViewItem(0)) {
|
||||||
auto volume_id = m_objects_model->GetVolumeIdByItem(item);
|
og_name = _(L("Object Settings to modify"));
|
||||||
m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->volumes[volume_id]->config);
|
m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->config);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
og_name = _(L("Part Settings to modify"));
|
||||||
|
is_part = true;
|
||||||
|
auto main_parent = m_objects_model->GetParent(parent);
|
||||||
|
obj_idx = m_objects_model->GetIdByItem(main_parent);
|
||||||
|
const auto volume_id = m_objects_model->GetVolumeIdByItem(parent);
|
||||||
|
m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->volumes[volume_id]->config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
og_name = _(L("Part manipulation"));
|
||||||
|
is_part = true;
|
||||||
|
const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||||
|
m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->volumes[volume_id]->config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto config = m_config;
|
og->set_name(" " + og_name + " ");
|
||||||
og->set_value("object_name", m_objects_model->GetName(item));
|
object_name = m_objects_model->GetName(item);
|
||||||
m_default_config = std::make_shared<DynamicPrintConfig>(*DynamicPrintConfig::new_from_defaults_keys(get_options(is_part)));
|
m_default_config = std::make_shared<DynamicPrintConfig>(*DynamicPrintConfig::new_from_defaults_keys(get_options(is_part)));
|
||||||
}
|
}
|
||||||
else {
|
og->set_value("object_name", object_name);
|
||||||
wxString empty_str = wxEmptyString;
|
|
||||||
og->set_value("object_name", empty_str);
|
|
||||||
m_config = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
update_settings_list();
|
update_settings_list();
|
||||||
|
|
||||||
@ -1561,9 +1763,15 @@ void update_position_values()
|
|||||||
auto og = get_optgroup(ogFrequentlyObjectSettings);
|
auto og = get_optgroup(ogFrequentlyObjectSettings);
|
||||||
auto instance = (*m_objects)[m_selected_object_id]->instances.front();
|
auto instance = (*m_objects)[m_selected_object_id]->instances.front();
|
||||||
|
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
og->set_value("position_x", int(instance->get_offset(X)));
|
||||||
|
og->set_value("position_y", int(instance->get_offset(Y)));
|
||||||
|
og->set_value("position_z", int(instance->get_offset(Z)));
|
||||||
|
#else
|
||||||
og->set_value("position_x", int(instance->offset(0)));
|
og->set_value("position_x", int(instance->offset(0)));
|
||||||
og->set_value("position_y", int(instance->offset(1)));
|
og->set_value("position_y", int(instance->offset(1)));
|
||||||
og->set_value("position_z", 0);
|
og->set_value("position_z", 0);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_position_values(const Vec3d& position)
|
void update_position_values(const Vec3d& position)
|
||||||
@ -1638,7 +1846,7 @@ void on_begin_drag(wxDataViewEvent &event)
|
|||||||
wxDataViewItem item(event.GetItem());
|
wxDataViewItem item(event.GetItem());
|
||||||
|
|
||||||
// only allow drags for item, not containers
|
// only allow drags for item, not containers
|
||||||
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
|
if (m_objects_model->GetParent(item) == wxDataViewItem(0) || m_objects_model->IsSettingsItem(item)) {
|
||||||
event.Veto();
|
event.Veto();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1662,7 +1870,7 @@ void on_drop_possible(wxDataViewEvent &event)
|
|||||||
|
|
||||||
// only allow drags for item or background, not containers
|
// only allow drags for item or background, not containers
|
||||||
if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
||||||
event.GetDataFormat() != wxDF_UNICODETEXT)
|
event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->IsSettingsItem(item))
|
||||||
event.Veto();
|
event.Veto();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1672,7 +1880,7 @@ void on_drop(wxDataViewEvent &event)
|
|||||||
|
|
||||||
// only allow drops for item, not containers
|
// only allow drops for item, not containers
|
||||||
if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
|
||||||
event.GetDataFormat() != wxDF_UNICODETEXT) {
|
event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->IsSettingsItem(item)) {
|
||||||
event.Veto();
|
event.Veto();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1700,6 +1908,9 @@ void on_drop(wxDataViewEvent &event)
|
|||||||
for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id+=delta, cnt++)
|
for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id+=delta, cnt++)
|
||||||
std::swap(volumes[id], volumes[id +delta]);
|
std::swap(volumes[id], volumes[id +delta]);
|
||||||
|
|
||||||
|
m_parts_changed = true;
|
||||||
|
parts_changed(m_selected_object_id);
|
||||||
|
|
||||||
g_prevent_list_events = false;
|
g_prevent_list_events = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1716,5 +1927,115 @@ void update_objects_list_extruder_column(int extruders_count)
|
|||||||
set_extruder_column_hidden(extruders_count <= 1);
|
set_extruder_column_hidden(extruders_count <= 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void create_double_slider(wxWindow* parent, wxBoxSizer* sizer, wxGLCanvas* canvas)
|
||||||
|
{
|
||||||
|
m_slider = new PrusaDoubleSlider(parent, wxID_ANY, 0, 0, 0, 100);
|
||||||
|
sizer->Add(m_slider, 0, wxEXPAND, 0);
|
||||||
|
|
||||||
|
m_preview_canvas = canvas;
|
||||||
|
m_preview_canvas->Bind(wxEVT_KEY_DOWN, update_double_slider_from_canvas);
|
||||||
|
|
||||||
|
m_slider->Bind(wxEVT_SCROLL_CHANGED, [parent](wxEvent& event) {
|
||||||
|
_3DScene::set_toolpaths_range(m_preview_canvas, m_slider->GetLowerValueD() - 1e-6, m_slider->GetHigherValueD() + 1e-6);
|
||||||
|
if (parent->IsShown())
|
||||||
|
m_preview_canvas->Refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_slider_values(std::vector<std::pair<int, double>> &values,
|
||||||
|
const std::vector<double> &layers_z)
|
||||||
|
{
|
||||||
|
std::vector<double> layers_all_z = _3DScene::get_current_print_zs(m_preview_canvas, false);
|
||||||
|
if (layers_all_z.size() == layers_z.size())
|
||||||
|
for (int i = 0; i < layers_z.size(); i++)
|
||||||
|
values.push_back(std::pair<int, double>(i+1, layers_z[i]));
|
||||||
|
else if (layers_all_z.size() > layers_z.size()) {
|
||||||
|
int cur_id = 0;
|
||||||
|
for (int i = 0; i < layers_z.size(); i++)
|
||||||
|
for (int j = cur_id; j < layers_all_z.size(); j++)
|
||||||
|
if (layers_z[i] - 1e-6 < layers_all_z[j] && layers_all_z[j] < layers_z[i] + 1e-6) {
|
||||||
|
values.push_back(std::pair<int, double>(j+1, layers_z[i]));
|
||||||
|
cur_id = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_double_slider_thumbs( const bool force_sliders_full_range,
|
||||||
|
const std::vector<double> &layers_z,
|
||||||
|
const double z_low, const double z_high)
|
||||||
|
{
|
||||||
|
// Force slider full range only when slider is created.
|
||||||
|
// Support selected diapason on the all next steps
|
||||||
|
if (/*force_sliders_full_range*/z_high == 0.0) {
|
||||||
|
m_slider->SetLowerValue(0);
|
||||||
|
m_slider->SetHigherValue(layers_z.size() - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = layers_z.size() - 1; i >= 0; i--)
|
||||||
|
if (z_low >= layers_z[i]) {
|
||||||
|
m_slider->SetLowerValue(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (int i = layers_z.size() - 1; i >= 0 ; i--)
|
||||||
|
if (z_high >= layers_z[i]) {
|
||||||
|
m_slider->SetHigherValue(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_double_slider(bool force_sliders_full_range)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<int, double>> values;
|
||||||
|
std::vector<double> layers_z = _3DScene::get_current_print_zs(m_preview_canvas, true);
|
||||||
|
fill_slider_values(values, layers_z);
|
||||||
|
|
||||||
|
const double z_low = m_slider->GetLowerValueD();
|
||||||
|
const double z_high = m_slider->GetHigherValueD();
|
||||||
|
m_slider->SetMaxValue(layers_z.size() - 1);
|
||||||
|
m_slider->SetSliderValues(values);
|
||||||
|
|
||||||
|
set_double_slider_thumbs(force_sliders_full_range, layers_z, z_low, z_high);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_double_slider()
|
||||||
|
{
|
||||||
|
m_slider->SetHigherValue(0);
|
||||||
|
m_slider->SetLowerValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_double_slider_from_canvas(wxKeyEvent& event)
|
||||||
|
{
|
||||||
|
if (event.HasModifiers()) {
|
||||||
|
event.Skip();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto key = event.GetKeyCode();
|
||||||
|
|
||||||
|
if (key == 'U' || key == 'D') {
|
||||||
|
const int new_pos = key == 'U' ? m_slider->GetHigherValue() + 1 : m_slider->GetHigherValue() - 1;
|
||||||
|
m_slider->SetHigherValue(new_pos);
|
||||||
|
if (event.ShiftDown()) m_slider->SetLowerValue(m_slider->GetHigherValue());
|
||||||
|
}
|
||||||
|
else if (key == 'S')
|
||||||
|
m_slider->ChangeOneLayerLock();
|
||||||
|
else
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_manipulation_sizer(const bool is_simple_mode)
|
||||||
|
{
|
||||||
|
auto item = m_objects_ctrl->GetSelection();
|
||||||
|
if (!item || !is_simple_mode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_objects_model->IsSettingsItem(item)) {
|
||||||
|
m_objects_ctrl->Select(m_objects_model->GetParent(item));
|
||||||
|
part_selection_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} //namespace GUI
|
} //namespace GUI
|
||||||
} //namespace Slic3r
|
} //namespace Slic3r
|
@ -9,13 +9,15 @@ class wxArrayString;
|
|||||||
class wxMenu;
|
class wxMenu;
|
||||||
class wxDataViewEvent;
|
class wxDataViewEvent;
|
||||||
class wxKeyEvent;
|
class wxKeyEvent;
|
||||||
class wxControl;
|
class wxGLCanvas;
|
||||||
|
class wxBitmap;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
class ModelObject;
|
class ModelObject;
|
||||||
class Model;
|
class Model;
|
||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
//class wxGLCanvas;
|
||||||
|
|
||||||
enum ogGroup{
|
enum ogGroup{
|
||||||
ogFrequentlyChangingParameters,
|
ogFrequentlyChangingParameters,
|
||||||
@ -44,6 +46,9 @@ struct OBJECT_PARAMETERS
|
|||||||
double slab_z = 0.0;
|
double slab_z = 0.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::map<std::string, wxBitmap> t_category_icon;
|
||||||
|
inline t_category_icon& get_category_icon();
|
||||||
|
|
||||||
void add_collapsible_panes(wxWindow* parent, wxBoxSizer* sizer);
|
void add_collapsible_panes(wxWindow* parent, wxBoxSizer* sizer);
|
||||||
void add_objects_list(wxWindow* parent, wxBoxSizer* sizer);
|
void add_objects_list(wxWindow* parent, wxBoxSizer* sizer);
|
||||||
void add_object_settings(wxWindow* parent, wxBoxSizer* sizer);
|
void add_object_settings(wxWindow* parent, wxBoxSizer* sizer);
|
||||||
@ -66,16 +71,17 @@ void set_object_count(int idx, int count);
|
|||||||
void unselect_objects();
|
void unselect_objects();
|
||||||
// Select current object in the list on c++ side
|
// Select current object in the list on c++ side
|
||||||
void select_current_object(int idx);
|
void select_current_object(int idx);
|
||||||
|
// Select current volume in the list on c++ side
|
||||||
|
void select_current_volume(int idx, int vol_idx);
|
||||||
// Remove objects/sub-object from the list
|
// Remove objects/sub-object from the list
|
||||||
void remove();
|
void remove();
|
||||||
|
|
||||||
//void create_double_slider(wxWindow* parent, wxControl* slider);
|
|
||||||
|
|
||||||
void object_ctrl_selection_changed();
|
void object_ctrl_selection_changed();
|
||||||
void object_ctrl_context_menu();
|
void object_ctrl_context_menu();
|
||||||
void object_ctrl_key_event(wxKeyEvent& event);
|
void object_ctrl_key_event(wxKeyEvent& event);
|
||||||
void object_ctrl_item_value_change(wxDataViewEvent& event);
|
void object_ctrl_item_value_change(wxDataViewEvent& event);
|
||||||
void show_context_menu();
|
void show_context_menu();
|
||||||
|
bool is_splittable_object(const bool split_part);
|
||||||
|
|
||||||
void init_mesh_icons();
|
void init_mesh_icons();
|
||||||
void set_event_object_selection_changed(const int& event);
|
void set_event_object_selection_changed(const int& event);
|
||||||
@ -87,13 +93,14 @@ void set_objects_from_model(Model &model);
|
|||||||
bool is_parts_changed();
|
bool is_parts_changed();
|
||||||
bool is_part_settings_changed();
|
bool is_part_settings_changed();
|
||||||
|
|
||||||
void load_part( wxWindow* parent, ModelObject* model_object,
|
void load_part( ModelObject* model_object,
|
||||||
wxArrayString& part_names, const bool is_modifier);
|
wxArrayString& part_names, const bool is_modifier);
|
||||||
|
|
||||||
void load_lambda(wxWindow* parent, ModelObject* model_object,
|
void load_lambda( ModelObject* model_object,
|
||||||
wxArrayString& part_names, const bool is_modifier);
|
wxArrayString& part_names, const bool is_modifier);
|
||||||
|
void load_lambda( const std::string& type_name);
|
||||||
|
|
||||||
void on_btn_load(wxWindow* parent, bool is_modifier = false, bool is_lambda = false);
|
void on_btn_load(bool is_modifier = false, bool is_lambda = false);
|
||||||
void on_btn_del();
|
void on_btn_del();
|
||||||
void on_btn_split(const bool split_part);
|
void on_btn_split(const bool split_part);
|
||||||
void on_btn_move_up();
|
void on_btn_move_up();
|
||||||
@ -126,6 +133,15 @@ void on_drop(wxDataViewEvent &event);
|
|||||||
// update extruder column for objects_ctrl according to extruders count
|
// update extruder column for objects_ctrl according to extruders count
|
||||||
void update_objects_list_extruder_column(int extruders_count);
|
void update_objects_list_extruder_column(int extruders_count);
|
||||||
|
|
||||||
|
// Create/Update/Reset double slider on 3dPreview
|
||||||
|
void create_double_slider(wxWindow* parent, wxBoxSizer* sizer, wxGLCanvas* canvas);
|
||||||
|
void update_double_slider(bool force_sliders_full_range);
|
||||||
|
void reset_double_slider();
|
||||||
|
// update DoubleSlider after keyDown in canvas
|
||||||
|
void update_double_slider_from_canvas(wxKeyEvent& event);
|
||||||
|
|
||||||
|
void show_manipulation_sizer(const bool is_simple_mode);
|
||||||
|
|
||||||
} //namespace GUI
|
} //namespace GUI
|
||||||
} //namespace Slic3r
|
} //namespace Slic3r
|
||||||
#endif //slic3r_GUI_ObjectParts_hpp_
|
#endif //slic3r_GUI_ObjectParts_hpp_
|
@ -10,10 +10,12 @@ namespace GUI
|
|||||||
{
|
{
|
||||||
static wxString dots("…", wxConvUTF8);
|
static wxString dots("…", wxConvUTF8);
|
||||||
|
|
||||||
LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
|
LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent,
|
||||||
|
const wxString type_name):
|
||||||
|
m_type_name(type_name)
|
||||||
{
|
{
|
||||||
Create(parent, wxID_ANY, _(L("Lambda Object")),
|
Create(parent, wxID_ANY, _(L("Lambda Object")),
|
||||||
wxDefaultPosition, wxDefaultSize,
|
parent->GetScreenPosition(), wxDefaultSize,
|
||||||
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||||
|
|
||||||
// instead of double dim[3] = { 1.0, 1.0, 1.0 };
|
// instead of double dim[3] = { 1.0, 1.0, 1.0 };
|
||||||
@ -24,11 +26,20 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
|
|||||||
sizer = new wxBoxSizer(wxVERTICAL);
|
sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
// modificator options
|
// modificator options
|
||||||
m_modificator_options_book = new wxChoicebook( this, wxID_ANY, wxDefaultPosition,
|
if (m_type_name == wxEmptyString) {
|
||||||
wxDefaultSize, wxCHB_TOP);
|
m_modificator_options_book = new wxChoicebook( this, wxID_ANY, wxDefaultPosition,
|
||||||
sizer->Add(m_modificator_options_book, 1, wxEXPAND| wxALL, 10);
|
wxDefaultSize, wxCHB_TOP);
|
||||||
|
sizer->Add(m_modificator_options_book, 1, wxEXPAND | wxALL, 10);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||||
|
sizer->Add(m_panel, 1, wxEXPAND | wxALL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigOptionDef def;
|
||||||
|
def.width = 70;
|
||||||
auto optgroup = init_modificator_options_page(_(L("Box")));
|
auto optgroup = init_modificator_options_page(_(L("Box")));
|
||||||
|
if (optgroup){
|
||||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
||||||
int opt_id = opt_key == "l" ? 0 :
|
int opt_id = opt_key == "l" ? 0 :
|
||||||
opt_key == "w" ? 1 :
|
opt_key == "w" ? 1 :
|
||||||
@ -37,8 +48,6 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
|
|||||||
object_parameters.dim[opt_id] = boost::any_cast<double>(value);
|
object_parameters.dim[opt_id] = boost::any_cast<double>(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
ConfigOptionDef def;
|
|
||||||
def.width = 70;
|
|
||||||
def.type = coFloat;
|
def.type = coFloat;
|
||||||
def.default_value = new ConfigOptionFloat{ 1.0 };
|
def.default_value = new ConfigOptionFloat{ 1.0 };
|
||||||
def.label = L("L");
|
def.label = L("L");
|
||||||
@ -52,8 +61,10 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
|
|||||||
def.label = L("H");
|
def.label = L("H");
|
||||||
option = Option(def, "h");
|
option = Option(def, "h");
|
||||||
optgroup->append_single_option_line(option);
|
optgroup->append_single_option_line(option);
|
||||||
|
}
|
||||||
|
|
||||||
optgroup = init_modificator_options_page(_(L("Cylinder")));
|
optgroup = init_modificator_options_page(_(L("Cylinder")));
|
||||||
|
if (optgroup){
|
||||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
||||||
int val = boost::any_cast<int>(value);
|
int val = boost::any_cast<int>(value);
|
||||||
if (opt_key == "cyl_r")
|
if (opt_key == "cyl_r")
|
||||||
@ -66,14 +77,16 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
|
|||||||
def.type = coInt;
|
def.type = coInt;
|
||||||
def.default_value = new ConfigOptionInt{ 1 };
|
def.default_value = new ConfigOptionInt{ 1 };
|
||||||
def.label = L("Radius");
|
def.label = L("Radius");
|
||||||
option = Option(def, "cyl_r");
|
auto option = Option(def, "cyl_r");
|
||||||
optgroup->append_single_option_line(option);
|
optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
def.label = L("Height");
|
def.label = L("Height");
|
||||||
option = Option(def, "cyl_h");
|
option = Option(def, "cyl_h");
|
||||||
optgroup->append_single_option_line(option);
|
optgroup->append_single_option_line(option);
|
||||||
|
}
|
||||||
|
|
||||||
optgroup = init_modificator_options_page(_(L("Sphere")));
|
optgroup = init_modificator_options_page(_(L("Sphere")));
|
||||||
|
if (optgroup){
|
||||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
||||||
if (opt_key == "sph_rho")
|
if (opt_key == "sph_rho")
|
||||||
object_parameters.sph_rho = boost::any_cast<double>(value);
|
object_parameters.sph_rho = boost::any_cast<double>(value);
|
||||||
@ -83,10 +96,12 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
|
|||||||
def.type = coFloat;
|
def.type = coFloat;
|
||||||
def.default_value = new ConfigOptionFloat{ 1.0 };
|
def.default_value = new ConfigOptionFloat{ 1.0 };
|
||||||
def.label = L("Rho");
|
def.label = L("Rho");
|
||||||
option = Option(def, "sph_rho");
|
auto option = Option(def, "sph_rho");
|
||||||
optgroup->append_single_option_line(option);
|
optgroup->append_single_option_line(option);
|
||||||
|
}
|
||||||
|
|
||||||
optgroup = init_modificator_options_page(_(L("Slab")));
|
optgroup = init_modificator_options_page(_(L("Slab")));
|
||||||
|
if (optgroup){
|
||||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
||||||
double val = boost::any_cast<double>(value);
|
double val = boost::any_cast<double>(value);
|
||||||
if (opt_key == "slab_z")
|
if (opt_key == "slab_z")
|
||||||
@ -96,13 +111,16 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
|
|||||||
else return;
|
else return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
def.type = coFloat;
|
||||||
|
def.default_value = new ConfigOptionFloat{ 1.0 };
|
||||||
def.label = L("H");
|
def.label = L("H");
|
||||||
option = Option(def, "slab_h");
|
auto option = Option(def, "slab_h");
|
||||||
optgroup->append_single_option_line(option);
|
optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
def.label = L("Initial Z");
|
def.label = L("Initial Z");
|
||||||
option = Option(def, "slab_z");
|
option = Option(def, "slab_z");
|
||||||
optgroup->append_single_option_line(option);
|
optgroup->append_single_option_line(option);
|
||||||
|
}
|
||||||
|
|
||||||
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e)
|
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e)
|
||||||
{
|
{
|
||||||
@ -127,8 +145,7 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const auto button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
|
||||||
auto button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
|
|
||||||
|
|
||||||
wxButton* btn_OK = static_cast<wxButton*>(FindWindowById(wxID_OK, this));
|
wxButton* btn_OK = static_cast<wxButton*>(FindWindowById(wxID_OK, this));
|
||||||
btn_OK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
btn_OK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||||
@ -155,9 +172,11 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
|
|||||||
|
|
||||||
// Called from the constructor.
|
// Called from the constructor.
|
||||||
// Create a panel for a rectangular / circular / custom bed shape.
|
// Create a panel for a rectangular / circular / custom bed shape.
|
||||||
ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(wxString title){
|
ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wxString& title){
|
||||||
|
if (!m_type_name.IsEmpty() && m_type_name != title)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
auto panel = new wxPanel(m_modificator_options_book);
|
auto panel = m_type_name.IsEmpty() ? new wxPanel(m_modificator_options_book) : m_panel;
|
||||||
|
|
||||||
ConfigOptionsGroupShp optgroup;
|
ConfigOptionsGroupShp optgroup;
|
||||||
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Add")) + " " +title + " " +dots);
|
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Add")) + " " +title + " " +dots);
|
||||||
@ -165,8 +184,12 @@ ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(wxString
|
|||||||
|
|
||||||
m_optgroups.push_back(optgroup);
|
m_optgroups.push_back(optgroup);
|
||||||
|
|
||||||
panel->SetSizerAndFit(optgroup->sizer);
|
if (m_type_name.IsEmpty()) {
|
||||||
m_modificator_options_book->AddPage(panel, title);
|
panel->SetSizerAndFit(optgroup->sizer);
|
||||||
|
m_modificator_options_book->AddPage(panel, title);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
panel->SetSizer(optgroup->sizer);
|
||||||
|
|
||||||
return optgroup;
|
return optgroup;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/choicebk.h>
|
#include <wx/choicebk.h>
|
||||||
|
|
||||||
|
class wxPanel;
|
||||||
|
|
||||||
namespace Slic3r
|
namespace Slic3r
|
||||||
{
|
{
|
||||||
namespace GUI
|
namespace GUI
|
||||||
@ -14,16 +16,19 @@ namespace GUI
|
|||||||
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
|
||||||
class LambdaObjectDialog : public wxDialog
|
class LambdaObjectDialog : public wxDialog
|
||||||
{
|
{
|
||||||
wxChoicebook* m_modificator_options_book;
|
wxChoicebook* m_modificator_options_book = nullptr;
|
||||||
std::vector <ConfigOptionsGroupShp> m_optgroups;
|
std::vector <ConfigOptionsGroupShp> m_optgroups;
|
||||||
|
wxString m_type_name;
|
||||||
|
wxPanel* m_panel = nullptr;
|
||||||
public:
|
public:
|
||||||
LambdaObjectDialog(wxWindow* parent);
|
LambdaObjectDialog(wxWindow* parent,
|
||||||
|
const wxString type_name = wxEmptyString);
|
||||||
~LambdaObjectDialog(){}
|
~LambdaObjectDialog(){}
|
||||||
|
|
||||||
bool CanClose() { return true; } // ???
|
bool CanClose() { return true; } // ???
|
||||||
OBJECT_PARAMETERS& ObjectParameters(){ return object_parameters; }
|
OBJECT_PARAMETERS& ObjectParameters(){ return object_parameters; }
|
||||||
|
|
||||||
ConfigOptionsGroupShp init_modificator_options_page(wxString title);
|
ConfigOptionsGroupShp init_modificator_options_page(const wxString& title);
|
||||||
|
|
||||||
// Note whether the window was already closed, so a pending update is not executed.
|
// Note whether the window was already closed, so a pending update is not executed.
|
||||||
bool m_already_closed = false;
|
bool m_already_closed = false;
|
||||||
|
@ -158,7 +158,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
|
|||||||
// if we have an extra column, build it
|
// if we have an extra column, build it
|
||||||
if (extra_column) {
|
if (extra_column) {
|
||||||
if (extra_column) {
|
if (extra_column) {
|
||||||
grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL, 0);
|
grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// if the callback provides no sizer for the extra cell, put a spacer
|
// if the callback provides no sizer for the extra cell, put a spacer
|
||||||
|
@ -161,8 +161,10 @@ public:
|
|||||||
ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) :
|
ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) :
|
||||||
m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt),
|
m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt),
|
||||||
staticbox(title!=""), m_flag(flag), extra_column(extra_clmn){
|
staticbox(title!=""), m_flag(flag), extra_column(extra_clmn){
|
||||||
stb = new wxStaticBox(_parent, wxID_ANY, title);
|
if (staticbox) {
|
||||||
stb->SetFont(bold_font());
|
stb = new wxStaticBox(_parent, wxID_ANY, title);
|
||||||
|
stb->SetFont(bold_font());
|
||||||
|
}
|
||||||
sizer = (staticbox ? new wxStaticBoxSizer(stb, wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
|
sizer = (staticbox ? new wxStaticBoxSizer(stb, wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
|
||||||
auto num_columns = 1U;
|
auto num_columns = 1U;
|
||||||
if (label_width != 0) num_columns++;
|
if (label_width != 0) num_columns++;
|
||||||
|
@ -295,7 +295,7 @@ const std::vector<std::string>& Preset::print_options()
|
|||||||
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
|
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
|
||||||
"bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
|
"bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
|
||||||
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height",
|
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height",
|
||||||
"min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers",
|
"min_skirt_length", "brim_width", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
|
||||||
"raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing",
|
"raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing",
|
||||||
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers",
|
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers",
|
||||||
"support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance",
|
"support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance",
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include <wx/string.h>
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,13 +42,13 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Message shown on the next status update.
|
/// Message shown on the next status update.
|
||||||
virtual void message(const wxString&) = 0;
|
virtual void message(const std::string&) = 0;
|
||||||
|
|
||||||
/// Title of the operation.
|
/// Title of the operation.
|
||||||
virtual void title(const wxString&) = 0;
|
virtual void title(const std::string&) = 0;
|
||||||
|
|
||||||
/// Formatted message for the next status update. Works just like sprintf.
|
/// Formatted message for the next status update. Works just like sprintf.
|
||||||
virtual void message_fmt(const wxString& fmt, ...);
|
virtual void message_fmt(const std::string& fmt, ...);
|
||||||
|
|
||||||
/// Set up a cancel callback for the operation if feasible.
|
/// Set up a cancel callback for the operation if feasible.
|
||||||
virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; }
|
virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; }
|
||||||
@ -62,7 +60,7 @@ public:
|
|||||||
virtual void cancel() { cancelfunc_(); }
|
virtual void cancel() { cancelfunc_(); }
|
||||||
|
|
||||||
/// Convenience function to call message and status update in one function.
|
/// Convenience function to call message and status update in one function.
|
||||||
void update(float st, const wxString& msg) {
|
void update(float st, const std::string& msg) {
|
||||||
message(msg); state(st);
|
message(msg); state(st);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -134,7 +134,7 @@ void ProgressStatusBar::embed(wxFrame *frame)
|
|||||||
mf->SetStatusBar(self);
|
mf->SetStatusBar(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProgressStatusBar::set_status_text(const std::string& txt)
|
void ProgressStatusBar::set_status_text(const wxString& txt)
|
||||||
{
|
{
|
||||||
self->SetStatusText(wxString::FromUTF8(txt.c_str()));
|
self->SetStatusText(wxString::FromUTF8(txt.c_str()));
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ class wxTimerEvent;
|
|||||||
class wxStatusBar;
|
class wxStatusBar;
|
||||||
class wxWindow;
|
class wxWindow;
|
||||||
class wxFrame;
|
class wxFrame;
|
||||||
|
class wxString;
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ public:
|
|||||||
inline void remove_cancel_callback() { set_cancel_callback(); }
|
inline void remove_cancel_callback() { set_cancel_callback(); }
|
||||||
void run(int rate);
|
void run(int rate);
|
||||||
void embed(wxFrame *frame = nullptr);
|
void embed(wxFrame *frame = nullptr);
|
||||||
void set_status_text(const std::string& txt);
|
void set_status_text(const wxString& txt);
|
||||||
|
|
||||||
// Temporary methods to satisfy Perl side
|
// Temporary methods to satisfy Perl side
|
||||||
void show_cancel_button();
|
void show_cancel_button();
|
||||||
|
@ -880,6 +880,7 @@ void TabPrint::build()
|
|||||||
page = add_options_page(_(L("Support material")), "building.png");
|
page = add_options_page(_(L("Support material")), "building.png");
|
||||||
optgroup = page->new_optgroup(_(L("Support material")));
|
optgroup = page->new_optgroup(_(L("Support material")));
|
||||||
optgroup->append_single_option_line("support_material");
|
optgroup->append_single_option_line("support_material");
|
||||||
|
optgroup->append_single_option_line("support_material_auto");
|
||||||
optgroup->append_single_option_line("support_material_threshold");
|
optgroup->append_single_option_line("support_material_threshold");
|
||||||
optgroup->append_single_option_line("support_material_enforce_layers");
|
optgroup->append_single_option_line("support_material_enforce_layers");
|
||||||
|
|
||||||
@ -1219,13 +1220,15 @@ void TabPrint::update()
|
|||||||
|
|
||||||
bool have_raft = m_config->opt_int("raft_layers") > 0;
|
bool have_raft = m_config->opt_int("raft_layers") > 0;
|
||||||
bool have_support_material = m_config->opt_bool("support_material") || have_raft;
|
bool have_support_material = m_config->opt_bool("support_material") || have_raft;
|
||||||
|
bool have_support_material_auto = have_support_material && m_config->opt_bool("support_material_auto");
|
||||||
bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0;
|
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;
|
bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0;
|
||||||
for (auto el : {"support_material_threshold", "support_material_pattern", "support_material_with_sheath",
|
for (auto el : {"support_material_pattern", "support_material_with_sheath",
|
||||||
"support_material_spacing", "support_material_angle", "support_material_interface_layers",
|
"support_material_spacing", "support_material_angle", "support_material_interface_layers",
|
||||||
"dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance",
|
"dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance",
|
||||||
"support_material_xy_spacing" })
|
"support_material_xy_spacing" })
|
||||||
get_field(el)->toggle(have_support_material);
|
get_field(el)->toggle(have_support_material);
|
||||||
|
get_field("support_material_threshold")->toggle(have_support_material_auto);
|
||||||
|
|
||||||
for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder",
|
for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder",
|
||||||
"support_material_interface_speed", "support_material_interface_contact_loops" })
|
"support_material_interface_speed", "support_material_interface_contact_loops" })
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "GUI.hpp"
|
#include "GUI.hpp"
|
||||||
#include "../../libslic3r/Utils.hpp"
|
#include "../../libslic3r/Utils.hpp"
|
||||||
|
#include "BitmapCache.hpp"
|
||||||
|
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/statline.h>
|
#include <wx/statline.h>
|
||||||
@ -357,11 +358,53 @@ void PrusaObjectDataViewModelNode::set_part_action_icon() {
|
|||||||
m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG);
|
m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr;
|
||||||
|
bool PrusaObjectDataViewModelNode::update_settings_digest(const std::vector<std::string>& categories)
|
||||||
|
{
|
||||||
|
if (m_type != "settings" || m_opt_categories == categories)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_opt_categories = categories;
|
||||||
|
m_name = wxEmptyString;
|
||||||
|
m_icon = m_empty_icon;
|
||||||
|
|
||||||
|
auto categories_icon = Slic3r::GUI::get_category_icon();
|
||||||
|
|
||||||
|
for (auto& cat : m_opt_categories)
|
||||||
|
m_name += cat + "; ";
|
||||||
|
|
||||||
|
wxBitmap *bmp = m_bitmap_cache->find(m_name.ToStdString());
|
||||||
|
if (bmp == nullptr) {
|
||||||
|
std::vector<wxBitmap> bmps;
|
||||||
|
for (auto& cat : m_opt_categories)
|
||||||
|
bmps.emplace_back(categories_icon.find(cat) == categories_icon.end() ?
|
||||||
|
wxNullBitmap : categories_icon.at(cat));
|
||||||
|
bmp = m_bitmap_cache->insert(m_name.ToStdString(), bmps);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_bmp = *bmp;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// *****************************************************************************
|
// *****************************************************************************
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// PrusaObjectDataViewModel
|
// PrusaObjectDataViewModel
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PrusaObjectDataViewModel::PrusaObjectDataViewModel()
|
||||||
|
{
|
||||||
|
m_bitmap_cache = new Slic3r::GUI::BitmapCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrusaObjectDataViewModel::~PrusaObjectDataViewModel()
|
||||||
|
{
|
||||||
|
for (auto object : m_objects)
|
||||||
|
delete object;
|
||||||
|
delete m_bitmap_cache;
|
||||||
|
m_bitmap_cache = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name)
|
wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name)
|
||||||
{
|
{
|
||||||
auto root = new PrusaObjectDataViewModelNode(name);
|
auto root = new PrusaObjectDataViewModelNode(name);
|
||||||
@ -386,34 +429,50 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name, const int ins
|
|||||||
|
|
||||||
wxDataViewItem PrusaObjectDataViewModel::AddChild( const wxDataViewItem &parent_item,
|
wxDataViewItem PrusaObjectDataViewModel::AddChild( const wxDataViewItem &parent_item,
|
||||||
const wxString &name,
|
const wxString &name,
|
||||||
const wxIcon& icon,
|
const wxBitmap& icon,
|
||||||
const int extruder/* = 0*/,
|
const int extruder/* = 0*/,
|
||||||
const bool create_frst_child/* = true*/)
|
const bool create_frst_child/* = true*/)
|
||||||
{
|
{
|
||||||
PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID();
|
PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID();
|
||||||
if (!root) return wxDataViewItem(0);
|
if (!root) return wxDataViewItem(0);
|
||||||
|
|
||||||
wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder);
|
const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder);
|
||||||
|
|
||||||
if (root->GetChildren().Count() == 0 && create_frst_child)
|
if (create_frst_child && (root->GetChildren().Count() == 0 ||
|
||||||
|
(root->GetChildren().Count() == 1 && root->GetNthChild(0)->m_type == "settings")))
|
||||||
{
|
{
|
||||||
auto icon_solid_mesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
|
const auto icon_solid_mesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);
|
||||||
auto node = new PrusaObjectDataViewModelNode(root, root->m_name, icon_solid_mesh, extruder_str, 0);
|
const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, icon_solid_mesh, extruder_str, 0);
|
||||||
root->Append(node);
|
root->Append(node);
|
||||||
// notify control
|
// notify control
|
||||||
wxDataViewItem child((void*)node);
|
const wxDataViewItem child((void*)node);
|
||||||
ItemAdded(parent_item, child);
|
ItemAdded(parent_item, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto volume_id = root->GetChildCount();
|
const auto volume_id = root->GetChildCount() > 0 && root->GetNthChild(0)->m_type == "settings" ?
|
||||||
auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, volume_id);
|
root->GetChildCount() - 1 : root->GetChildCount();
|
||||||
|
|
||||||
|
const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, volume_id);
|
||||||
root->Append(node);
|
root->Append(node);
|
||||||
// notify control
|
// notify control
|
||||||
wxDataViewItem child((void*)node);
|
const wxDataViewItem child((void*)node);
|
||||||
ItemAdded(parent_item, child);
|
ItemAdded(parent_item, child);
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item)
|
||||||
|
{
|
||||||
|
PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID();
|
||||||
|
if (!root) return wxDataViewItem(0);
|
||||||
|
|
||||||
|
const auto node = new PrusaObjectDataViewModelNode(root);
|
||||||
|
root->Insert(node, 0);
|
||||||
|
// notify control
|
||||||
|
const wxDataViewItem child((void*)node);
|
||||||
|
ItemAdded(parent_item, child);
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item)
|
wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item)
|
||||||
{
|
{
|
||||||
auto ret_item = wxDataViewItem(0);
|
auto ret_item = wxDataViewItem(0);
|
||||||
@ -438,7 +497,7 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item)
|
|||||||
|
|
||||||
//update volume_id value for remaining child-nodes
|
//update volume_id value for remaining child-nodes
|
||||||
auto children = node_parent->GetChildren();
|
auto children = node_parent->GetChildren();
|
||||||
for (size_t i = 0; i < node_parent->GetChildCount(); i++)
|
for (size_t i = 0; i < node_parent->GetChildCount() && v_id>=0; i++)
|
||||||
{
|
{
|
||||||
auto volume_id = children[i]->GetVolumeId();
|
auto volume_id = children[i]->GetVolumeId();
|
||||||
if (volume_id > v_id)
|
if (volume_id > v_id)
|
||||||
@ -460,9 +519,10 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item)
|
|||||||
delete node;
|
delete node;
|
||||||
|
|
||||||
// set m_containet to FALSE if parent has no child
|
// set m_containet to FALSE if parent has no child
|
||||||
if (node_parent && node_parent->GetChildCount() == 0){
|
if (node_parent) {
|
||||||
#ifndef __WXGTK__
|
#ifndef __WXGTK__
|
||||||
node_parent->m_container = false;
|
if (node_parent->GetChildCount() == 0)
|
||||||
|
node_parent->m_container = false;
|
||||||
#endif //__WXGTK__
|
#endif //__WXGTK__
|
||||||
ret_item = parent;
|
ret_item = parent;
|
||||||
}
|
}
|
||||||
@ -522,6 +582,26 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx)
|
||||||
|
{
|
||||||
|
if (obj_idx >= m_objects.size()) {
|
||||||
|
printf("Error! Out of objects range.\n");
|
||||||
|
return wxDataViewItem(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto parent = m_objects[obj_idx];
|
||||||
|
if (parent->GetChildCount() == 0) {
|
||||||
|
printf("Error! Object has no one volume.\n");
|
||||||
|
return wxDataViewItem(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < parent->GetChildCount(); i++)
|
||||||
|
if (parent->GetNthChild(i)->m_volume_id == volume_idx)
|
||||||
|
return wxDataViewItem(parent->GetNthChild(i));
|
||||||
|
|
||||||
|
return wxDataViewItem(0);
|
||||||
|
}
|
||||||
|
|
||||||
int PrusaObjectDataViewModel::GetIdByItem(wxDataViewItem& item)
|
int PrusaObjectDataViewModel::GetIdByItem(wxDataViewItem& item)
|
||||||
{
|
{
|
||||||
wxASSERT(item.IsOk());
|
wxASSERT(item.IsOk());
|
||||||
@ -534,7 +614,7 @@ int PrusaObjectDataViewModel::GetIdByItem(wxDataViewItem& item)
|
|||||||
return it - m_objects.begin();
|
return it - m_objects.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
int PrusaObjectDataViewModel::GetVolumeIdByItem(wxDataViewItem& item)
|
int PrusaObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item)
|
||||||
{
|
{
|
||||||
wxASSERT(item.IsOk());
|
wxASSERT(item.IsOk());
|
||||||
|
|
||||||
@ -568,6 +648,12 @@ wxIcon& PrusaObjectDataViewModel::GetIcon(const wxDataViewItem &item) const
|
|||||||
return node->m_icon;
|
return node->m_icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxBitmap& PrusaObjectDataViewModel::GetBitmap(const wxDataViewItem &item) const
|
||||||
|
{
|
||||||
|
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
|
||||||
|
return node->m_bmp;
|
||||||
|
}
|
||||||
|
|
||||||
void PrusaObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const
|
void PrusaObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const
|
||||||
{
|
{
|
||||||
wxASSERT(item.IsOk());
|
wxASSERT(item.IsOk());
|
||||||
@ -576,8 +662,8 @@ void PrusaObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem
|
|||||||
switch (col)
|
switch (col)
|
||||||
{
|
{
|
||||||
case 0:{
|
case 0:{
|
||||||
const wxDataViewIconText data(node->m_name, node->m_icon);
|
const PrusaDataViewBitmapText data(node->m_name, node->m_bmp);
|
||||||
variant << data;
|
variant << data;
|
||||||
break;}
|
break;}
|
||||||
case 1:
|
case 1:
|
||||||
variant = node->m_copy;
|
variant = node->m_copy;
|
||||||
@ -667,26 +753,35 @@ wxDataViewItem PrusaObjectDataViewModel::ReorganizeChildren(int current_volume_i
|
|||||||
if (!node_parent) // happens if item.IsOk()==false
|
if (!node_parent) // happens if item.IsOk()==false
|
||||||
return ret_item;
|
return ret_item;
|
||||||
|
|
||||||
PrusaObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id);
|
const size_t shift = node_parent->GetChildren().Item(0)->m_type == "settings" ? 1 : 0;
|
||||||
|
|
||||||
|
PrusaObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id+shift);
|
||||||
node_parent->GetChildren().Remove(deleted_node);
|
node_parent->GetChildren().Remove(deleted_node);
|
||||||
ItemDeleted(parent, wxDataViewItem(deleted_node));
|
ItemDeleted(parent, wxDataViewItem(deleted_node));
|
||||||
node_parent->Insert(deleted_node, new_volume_id);
|
node_parent->Insert(deleted_node, new_volume_id+shift);
|
||||||
ItemAdded(parent, wxDataViewItem(deleted_node));
|
ItemAdded(parent, wxDataViewItem(deleted_node));
|
||||||
|
const auto settings_item = HasSettings(wxDataViewItem(deleted_node));
|
||||||
|
if (settings_item)
|
||||||
|
ItemAdded(wxDataViewItem(deleted_node), settings_item);
|
||||||
|
|
||||||
//update volume_id value for child-nodes
|
//update volume_id value for child-nodes
|
||||||
auto children = node_parent->GetChildren();
|
auto children = node_parent->GetChildren();
|
||||||
int id_frst = current_volume_id < new_volume_id ? current_volume_id : new_volume_id;
|
int id_frst = current_volume_id < new_volume_id ? current_volume_id : new_volume_id;
|
||||||
int id_last = current_volume_id > new_volume_id ? current_volume_id : new_volume_id;
|
int id_last = current_volume_id > new_volume_id ? current_volume_id : new_volume_id;
|
||||||
for (int id = id_frst; id <= id_last; ++id)
|
for (int id = id_frst; id <= id_last; ++id)
|
||||||
children[id]->SetVolumeId(id);
|
children[id+shift]->SetVolumeId(id);
|
||||||
|
|
||||||
return wxDataViewItem(node_parent->GetNthChild(new_volume_id));
|
return wxDataViewItem(node_parent->GetNthChild(new_volume_id+shift));
|
||||||
}
|
}
|
||||||
|
|
||||||
// bool MyObjectTreeModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const
|
bool PrusaObjectDataViewModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const
|
||||||
// {
|
{
|
||||||
//
|
wxASSERT(item.IsOk());
|
||||||
// }
|
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
|
||||||
|
|
||||||
|
// disable extruder selection for the "Settings" item
|
||||||
|
return !(col == 2 && node->m_extruder.IsEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
wxDataViewItem PrusaObjectDataViewModel::GetParent(const wxDataViewItem &item) const
|
wxDataViewItem PrusaObjectDataViewModel::GetParent(const wxDataViewItem &item) const
|
||||||
{
|
{
|
||||||
@ -738,6 +833,87 @@ unsigned int PrusaObjectDataViewModel::GetChildren(const wxDataViewItem &parent,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxDataViewItem PrusaObjectDataViewModel::HasSettings(const wxDataViewItem &item) const
|
||||||
|
{
|
||||||
|
if (!item.IsOk())
|
||||||
|
return wxDataViewItem(0);
|
||||||
|
|
||||||
|
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
|
||||||
|
if (node->GetChildCount() == 0)
|
||||||
|
return wxDataViewItem(0);
|
||||||
|
|
||||||
|
auto& children = node->GetChildren();
|
||||||
|
if (children[0]->m_type == "settings")
|
||||||
|
return wxDataViewItem((void*)children[0]);;
|
||||||
|
|
||||||
|
return wxDataViewItem(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrusaObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const
|
||||||
|
{
|
||||||
|
if (!item.IsOk())
|
||||||
|
return false;
|
||||||
|
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
|
||||||
|
return node->m_type == "settings";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void PrusaObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item,
|
||||||
|
const std::vector<std::string>& categories)
|
||||||
|
{
|
||||||
|
if (!item.IsOk()) return;
|
||||||
|
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
|
||||||
|
if (!node->update_settings_digest(categories))
|
||||||
|
return;
|
||||||
|
ItemChanged(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
IMPLEMENT_VARIANT_OBJECT(PrusaDataViewBitmapText)
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// PrusaIconTextRenderer
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
bool PrusaBitmapTextRenderer::SetValue(const wxVariant &value)
|
||||||
|
{
|
||||||
|
m_value << value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrusaBitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrusaBitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state)
|
||||||
|
{
|
||||||
|
int xoffset = 0;
|
||||||
|
|
||||||
|
const wxBitmap& icon = m_value.GetBitmap();
|
||||||
|
if (icon.IsOk())
|
||||||
|
{
|
||||||
|
dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2);
|
||||||
|
xoffset = icon.GetWidth() + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderText(m_value.GetText(), xoffset, rect, dc, state);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxSize PrusaBitmapTextRenderer::GetSize() const
|
||||||
|
{
|
||||||
|
if (!m_value.GetText().empty())
|
||||||
|
{
|
||||||
|
wxSize size = GetTextExtent(m_value.GetText());
|
||||||
|
|
||||||
|
if (m_value.GetBitmap().IsOk())
|
||||||
|
size.x += m_value.GetBitmap().GetWidth() + 4;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
return wxSize(80, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// PrusaDoubleSlider
|
// PrusaDoubleSlider
|
||||||
@ -829,16 +1005,28 @@ wxSize PrusaDoubleSlider::DoGetBestSize() const
|
|||||||
|
|
||||||
void PrusaDoubleSlider::SetLowerValue(const int lower_val)
|
void PrusaDoubleSlider::SetLowerValue(const int lower_val)
|
||||||
{
|
{
|
||||||
|
m_selection = ssLower;
|
||||||
m_lower_value = lower_val;
|
m_lower_value = lower_val;
|
||||||
|
correct_lower_value();
|
||||||
Refresh();
|
Refresh();
|
||||||
Update();
|
Update();
|
||||||
|
|
||||||
|
wxCommandEvent e(wxEVT_SCROLL_CHANGED);
|
||||||
|
e.SetEventObject(this);
|
||||||
|
ProcessWindowEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrusaDoubleSlider::SetHigherValue(const int higher_val)
|
void PrusaDoubleSlider::SetHigherValue(const int higher_val)
|
||||||
{
|
{
|
||||||
|
m_selection = ssHigher;
|
||||||
m_higher_value = higher_val;
|
m_higher_value = higher_val;
|
||||||
|
correct_higher_value();
|
||||||
Refresh();
|
Refresh();
|
||||||
Update();
|
Update();
|
||||||
|
|
||||||
|
wxCommandEvent e(wxEVT_SCROLL_CHANGED);
|
||||||
|
e.SetEventObject(this);
|
||||||
|
ProcessWindowEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrusaDoubleSlider::SetMaxValue(const int max_value)
|
void PrusaDoubleSlider::SetMaxValue(const int max_value)
|
||||||
@ -905,6 +1093,13 @@ void PrusaDoubleSlider::get_size(int *w, int *h)
|
|||||||
is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim;
|
is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double PrusaDoubleSlider::get_double_value(const SelectedSlider& selection) const
|
||||||
|
{
|
||||||
|
if (m_values.empty())
|
||||||
|
return 0.0;
|
||||||
|
return m_values[selection == ssLower ? m_lower_value : m_higher_value].second;
|
||||||
|
}
|
||||||
|
|
||||||
void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos)
|
void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos)
|
||||||
{
|
{
|
||||||
const double step = get_scroll_step();
|
const double step = get_scroll_step();
|
||||||
@ -1010,7 +1205,7 @@ wxString PrusaDoubleSlider::get_label(const SelectedSlider& selection) const
|
|||||||
|
|
||||||
void PrusaDoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const
|
void PrusaDoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const
|
||||||
{
|
{
|
||||||
if (m_is_one_layer && selection != m_selection || !selection)
|
if ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection || !selection)
|
||||||
return;
|
return;
|
||||||
wxCoord text_width, text_height;
|
wxCoord text_width, text_height;
|
||||||
const wxString label = get_label(selection);
|
const wxString label = get_label(selection);
|
||||||
@ -1178,6 +1373,20 @@ bool PrusaDoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrusaDoubleSlider::ChangeOneLayerLock()
|
||||||
|
{
|
||||||
|
m_is_one_layer = !m_is_one_layer;
|
||||||
|
m_selection == ssLower ? correct_lower_value() : correct_higher_value();
|
||||||
|
if (!m_selection) m_selection = ssHigher;
|
||||||
|
|
||||||
|
Refresh();
|
||||||
|
Update();
|
||||||
|
|
||||||
|
wxCommandEvent e(wxEVT_SCROLL_CHANGED);
|
||||||
|
e.SetEventObject(this);
|
||||||
|
ProcessWindowEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
void PrusaDoubleSlider::OnLeftDown(wxMouseEvent& event)
|
void PrusaDoubleSlider::OnLeftDown(wxMouseEvent& event)
|
||||||
{
|
{
|
||||||
this->CaptureMouse();
|
this->CaptureMouse();
|
||||||
@ -1245,6 +1454,10 @@ void PrusaDoubleSlider::OnMotion(wxMouseEvent& event)
|
|||||||
Refresh();
|
Refresh();
|
||||||
Update();
|
Update();
|
||||||
event.Skip();
|
event.Skip();
|
||||||
|
|
||||||
|
wxCommandEvent e(wxEVT_SCROLL_CHANGED);
|
||||||
|
e.SetEventObject(this);
|
||||||
|
ProcessWindowEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrusaDoubleSlider::OnLeftUp(wxMouseEvent& event)
|
void PrusaDoubleSlider::OnLeftUp(wxMouseEvent& event)
|
||||||
|
@ -144,6 +144,49 @@ public:
|
|||||||
#endif //__WXMSW__
|
#endif //__WXMSW__
|
||||||
|
|
||||||
// *****************************************************************************
|
// *****************************************************************************
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// PrusaDataViewBitmapText: helper class used by PrusaBitmapTextRenderer
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class PrusaDataViewBitmapText : public wxObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrusaDataViewBitmapText(const wxString &text = wxEmptyString,
|
||||||
|
const wxBitmap& bmp = wxNullBitmap) :
|
||||||
|
m_text(text), m_bmp(bmp)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
PrusaDataViewBitmapText(const PrusaDataViewBitmapText &other)
|
||||||
|
: wxObject(),
|
||||||
|
m_text(other.m_text),
|
||||||
|
m_bmp(other.m_bmp)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void SetText(const wxString &text) { m_text = text; }
|
||||||
|
wxString GetText() const { return m_text; }
|
||||||
|
void SetBitmap(const wxIcon &icon) { m_bmp = icon; }
|
||||||
|
const wxBitmap &GetBitmap() const { return m_bmp; }
|
||||||
|
|
||||||
|
bool IsSameAs(const PrusaDataViewBitmapText& other) const {
|
||||||
|
return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const PrusaDataViewBitmapText& other) const {
|
||||||
|
return IsSameAs(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const PrusaDataViewBitmapText& other) const {
|
||||||
|
return !IsSameAs(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
wxString m_text;
|
||||||
|
wxBitmap m_bmp;
|
||||||
|
};
|
||||||
|
DECLARE_VARIANT_OBJECT(PrusaDataViewBitmapText)
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel
|
// PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -155,7 +198,9 @@ class PrusaObjectDataViewModelNode
|
|||||||
{
|
{
|
||||||
PrusaObjectDataViewModelNode* m_parent;
|
PrusaObjectDataViewModelNode* m_parent;
|
||||||
MyObjectTreeModelNodePtrArray m_children;
|
MyObjectTreeModelNodePtrArray m_children;
|
||||||
wxIcon m_empty_icon;
|
wxIcon m_empty_icon;
|
||||||
|
wxBitmap m_empty_bmp;
|
||||||
|
std::vector< std::string > m_opt_categories;
|
||||||
public:
|
public:
|
||||||
PrusaObjectDataViewModelNode(const wxString &name, const int instances_count=1) {
|
PrusaObjectDataViewModelNode(const wxString &name, const int instances_count=1) {
|
||||||
m_parent = NULL;
|
m_parent = NULL;
|
||||||
@ -174,18 +219,31 @@ public:
|
|||||||
|
|
||||||
PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent,
|
PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent,
|
||||||
const wxString& sub_obj_name,
|
const wxString& sub_obj_name,
|
||||||
const wxIcon& icon,
|
const wxBitmap& bmp,
|
||||||
const wxString& extruder,
|
const wxString& extruder,
|
||||||
const int volume_id=-1) {
|
const int volume_id=-1) {
|
||||||
m_parent = parent;
|
m_parent = parent;
|
||||||
m_name = sub_obj_name;
|
m_name = sub_obj_name;
|
||||||
m_copy = wxEmptyString;
|
m_copy = wxEmptyString;
|
||||||
m_icon = icon;
|
m_bmp = bmp;
|
||||||
m_type = "volume";
|
m_type = "volume";
|
||||||
m_volume_id = volume_id;
|
m_volume_id = volume_id;
|
||||||
m_extruder = extruder;
|
m_extruder = extruder;
|
||||||
|
#ifdef __WXGTK__
|
||||||
|
// it's necessary on GTK because of control have to know if this item will be container
|
||||||
|
// in another case you couldn't to add subitem for this item
|
||||||
|
// it will be produce "segmentation fault"
|
||||||
|
m_container = true;
|
||||||
|
#endif //__WXGTK__
|
||||||
set_part_action_icon();
|
set_part_action_icon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent) :
|
||||||
|
m_parent(parent),
|
||||||
|
m_name("Settings to modified"),
|
||||||
|
m_copy(wxEmptyString),
|
||||||
|
m_type("settings"),
|
||||||
|
m_extruder(wxEmptyString) {}
|
||||||
|
|
||||||
~PrusaObjectDataViewModelNode()
|
~PrusaObjectDataViewModelNode()
|
||||||
{
|
{
|
||||||
@ -200,9 +258,10 @@ public:
|
|||||||
|
|
||||||
wxString m_name;
|
wxString m_name;
|
||||||
wxIcon& m_icon = m_empty_icon;
|
wxIcon& m_icon = m_empty_icon;
|
||||||
|
wxBitmap& m_bmp = m_empty_bmp;
|
||||||
wxString m_copy;
|
wxString m_copy;
|
||||||
std::string m_type;
|
std::string m_type;
|
||||||
int m_volume_id;
|
int m_volume_id = -2;
|
||||||
bool m_container = false;
|
bool m_container = false;
|
||||||
wxString m_extruder = "default";
|
wxString m_extruder = "default";
|
||||||
wxBitmap m_action_icon;
|
wxBitmap m_action_icon;
|
||||||
@ -226,6 +285,8 @@ public:
|
|||||||
}
|
}
|
||||||
void Insert(PrusaObjectDataViewModelNode* child, unsigned int n)
|
void Insert(PrusaObjectDataViewModelNode* child, unsigned int n)
|
||||||
{
|
{
|
||||||
|
if (!m_container)
|
||||||
|
m_container = true;
|
||||||
m_children.Insert(child, n);
|
m_children.Insert(child, n);
|
||||||
}
|
}
|
||||||
void Append(PrusaObjectDataViewModelNode* child)
|
void Append(PrusaObjectDataViewModelNode* child)
|
||||||
@ -258,9 +319,9 @@ public:
|
|||||||
switch (col)
|
switch (col)
|
||||||
{
|
{
|
||||||
case 0:{
|
case 0:{
|
||||||
wxDataViewIconText data;
|
PrusaDataViewBitmapText data;
|
||||||
data << variant;
|
data << variant;
|
||||||
m_icon = data.GetIcon();
|
m_bmp = data.GetBitmap();
|
||||||
m_name = data.GetText();
|
m_name = data.GetText();
|
||||||
return true;}
|
return true;}
|
||||||
case 1:
|
case 1:
|
||||||
@ -281,6 +342,11 @@ public:
|
|||||||
{
|
{
|
||||||
m_icon = icon;
|
m_icon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetBitmap(const wxBitmap &icon)
|
||||||
|
{
|
||||||
|
m_bmp = icon;
|
||||||
|
}
|
||||||
|
|
||||||
void SetType(const std::string& type){
|
void SetType(const std::string& type){
|
||||||
m_type = type;
|
m_type = type;
|
||||||
@ -326,6 +392,7 @@ public:
|
|||||||
// Set action icons for node
|
// Set action icons for node
|
||||||
void set_object_action_icon();
|
void set_object_action_icon();
|
||||||
void set_part_action_icon();
|
void set_part_action_icon();
|
||||||
|
bool update_settings_digest(const std::vector<std::string>& categories);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -336,26 +403,24 @@ class PrusaObjectDataViewModel :public wxDataViewModel
|
|||||||
{
|
{
|
||||||
std::vector<PrusaObjectDataViewModelNode*> m_objects;
|
std::vector<PrusaObjectDataViewModelNode*> m_objects;
|
||||||
public:
|
public:
|
||||||
PrusaObjectDataViewModel(){}
|
PrusaObjectDataViewModel();
|
||||||
~PrusaObjectDataViewModel()
|
~PrusaObjectDataViewModel();
|
||||||
{
|
|
||||||
for (auto object : m_objects)
|
|
||||||
delete object;
|
|
||||||
}
|
|
||||||
|
|
||||||
wxDataViewItem Add(const wxString &name);
|
wxDataViewItem Add(const wxString &name);
|
||||||
wxDataViewItem Add(const wxString &name, const int instances_count);
|
wxDataViewItem Add(const wxString &name, const int instances_count);
|
||||||
wxDataViewItem AddChild(const wxDataViewItem &parent_item,
|
wxDataViewItem AddChild(const wxDataViewItem &parent_item,
|
||||||
const wxString &name,
|
const wxString &name,
|
||||||
const wxIcon& icon,
|
const wxBitmap& icon,
|
||||||
const int = 0,
|
const int extruder = 0,
|
||||||
const bool create_frst_child = true);
|
const bool create_frst_child = true);
|
||||||
|
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
|
||||||
wxDataViewItem Delete(const wxDataViewItem &item);
|
wxDataViewItem Delete(const wxDataViewItem &item);
|
||||||
void DeleteAll();
|
void DeleteAll();
|
||||||
void DeleteChildren(wxDataViewItem& parent);
|
void DeleteChildren(wxDataViewItem& parent);
|
||||||
wxDataViewItem GetItemById(int obj_idx);
|
wxDataViewItem GetItemById(int obj_idx);
|
||||||
|
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
|
||||||
int GetIdByItem(wxDataViewItem& item);
|
int GetIdByItem(wxDataViewItem& item);
|
||||||
int GetVolumeIdByItem(wxDataViewItem& item);
|
int GetVolumeIdByItem(const wxDataViewItem& item);
|
||||||
bool IsEmpty() { return m_objects.empty(); }
|
bool IsEmpty() { return m_objects.empty(); }
|
||||||
|
|
||||||
// helper method for wxLog
|
// helper method for wxLog
|
||||||
@ -363,6 +428,7 @@ public:
|
|||||||
wxString GetName(const wxDataViewItem &item) const;
|
wxString GetName(const wxDataViewItem &item) const;
|
||||||
wxString GetCopy(const wxDataViewItem &item) const;
|
wxString GetCopy(const wxDataViewItem &item) const;
|
||||||
wxIcon& GetIcon(const wxDataViewItem &item) const;
|
wxIcon& GetIcon(const wxDataViewItem &item) const;
|
||||||
|
wxBitmap& GetBitmap(const wxDataViewItem &item) const;
|
||||||
|
|
||||||
// helper methods to change the model
|
// helper methods to change the model
|
||||||
|
|
||||||
@ -383,8 +449,7 @@ public:
|
|||||||
int new_volume_id,
|
int new_volume_id,
|
||||||
const wxDataViewItem &parent);
|
const wxDataViewItem &parent);
|
||||||
|
|
||||||
// virtual bool IsEnabled(const wxDataViewItem &item,
|
virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override;
|
||||||
// unsigned int col) const override;
|
|
||||||
|
|
||||||
virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override;
|
virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override;
|
||||||
virtual bool IsContainer(const wxDataViewItem &item) const override;
|
virtual bool IsContainer(const wxDataViewItem &item) const override;
|
||||||
@ -394,8 +459,35 @@ public:
|
|||||||
// Is the container just a header or an item with all columns
|
// Is the container just a header or an item with all columns
|
||||||
// In our case it is an item with all columns
|
// In our case it is an item with all columns
|
||||||
virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
|
virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
|
||||||
|
|
||||||
|
wxDataViewItem HasSettings(const wxDataViewItem &item) const;
|
||||||
|
bool IsSettingsItem(const wxDataViewItem &item) const;
|
||||||
|
void UpdateSettingsDigest(const wxDataViewItem &item, const std::vector<std::string>& categories);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// PrusaBitmapTextRenderer
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT,
|
||||||
|
int align = wxDVR_DEFAULT_ALIGNMENT):
|
||||||
|
wxDataViewCustomRenderer(wxT("wxObject"), mode, align) {}
|
||||||
|
|
||||||
|
bool SetValue(const wxVariant &value);
|
||||||
|
bool GetValue(wxVariant &value) const;
|
||||||
|
|
||||||
|
virtual bool Render(wxRect cell, wxDC *dc, int state);
|
||||||
|
virtual wxSize GetSize() const;
|
||||||
|
|
||||||
|
virtual bool HasEditorCtrl() const { return false; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// wxDataViewIconText m_value;
|
||||||
|
PrusaDataViewBitmapText m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -516,7 +608,7 @@ public:
|
|||||||
int maxValue,
|
int maxValue,
|
||||||
const wxPoint& pos = wxDefaultPosition,
|
const wxPoint& pos = wxDefaultPosition,
|
||||||
const wxSize& size = wxDefaultSize,
|
const wxSize& size = wxDefaultSize,
|
||||||
long style = wxSL_HORIZONTAL,
|
long style = wxSL_VERTICAL,
|
||||||
const wxValidator& val = wxDefaultValidator,
|
const wxValidator& val = wxDefaultValidator,
|
||||||
const wxString& name = wxEmptyString);
|
const wxString& name = wxEmptyString);
|
||||||
~PrusaDoubleSlider(){}
|
~PrusaDoubleSlider(){}
|
||||||
@ -528,6 +620,8 @@ public:
|
|||||||
return m_higher_value;
|
return m_higher_value;
|
||||||
}
|
}
|
||||||
int GetActiveValue() const;
|
int GetActiveValue() const;
|
||||||
|
double GetLowerValueD() const { return get_double_value(ssLower); }
|
||||||
|
double GetHigherValueD() const { return get_double_value(ssHigher); }
|
||||||
wxSize DoGetBestSize() const override;
|
wxSize DoGetBestSize() const override;
|
||||||
void SetLowerValue(const int lower_val);
|
void SetLowerValue(const int lower_val);
|
||||||
void SetHigherValue(const int higher_val);
|
void SetHigherValue(const int higher_val);
|
||||||
@ -538,6 +632,7 @@ public:
|
|||||||
void SetSliderValues(const std::vector<std::pair<int, double>>& values) {
|
void SetSliderValues(const std::vector<std::pair<int, double>>& values) {
|
||||||
m_values = values;
|
m_values = values;
|
||||||
}
|
}
|
||||||
|
void ChangeOneLayerLock();
|
||||||
|
|
||||||
void OnPaint(wxPaintEvent& ){ render();}
|
void OnPaint(wxPaintEvent& ){ render();}
|
||||||
void OnLeftDown(wxMouseEvent& event);
|
void OnLeftDown(wxMouseEvent& event);
|
||||||
@ -583,6 +678,7 @@ protected:
|
|||||||
wxCoord get_position_from_value(const int value);
|
wxCoord get_position_from_value(const int value);
|
||||||
wxSize get_size();
|
wxSize get_size();
|
||||||
void get_size(int *w, int *h);
|
void get_size(int *w, int *h);
|
||||||
|
double get_double_value(const SelectedSlider& selection) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_min_value;
|
int m_min_value;
|
||||||
|
@ -231,7 +231,12 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
|
|||||||
spi.port = path;
|
spi.port = path;
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
auto friendly_name = sysfs_tty_prop(name, "product");
|
auto friendly_name = sysfs_tty_prop(name, "product");
|
||||||
spi.friendly_name = friendly_name ? (boost::format("%1% (%2%)") % *friendly_name % path).str() : path;
|
if (friendly_name) {
|
||||||
|
spi.is_printer = looks_like_printer(*friendly_name);
|
||||||
|
spi.friendly_name = (boost::format("%1% (%2%)") % *friendly_name % path).str();
|
||||||
|
} else {
|
||||||
|
spi.friendly_name = path;
|
||||||
|
}
|
||||||
auto vid = sysfs_tty_prop_hex(name, "idVendor");
|
auto vid = sysfs_tty_prop_hex(name, "idVendor");
|
||||||
auto pid = sysfs_tty_prop_hex(name, "idProduct");
|
auto pid = sysfs_tty_prop_hex(name, "idProduct");
|
||||||
if (vid && pid) {
|
if (vid && pid) {
|
||||||
|
@ -96,26 +96,21 @@ void add_frequently_changed_parameters(SV *ui_parent, SV *ui_sizer, SV *ui_p_siz
|
|||||||
void set_print_callback_event(Print *print, int id)
|
void set_print_callback_event(Print *print, int id)
|
||||||
%code%{ Slic3r::GUI::set_print_callback_event(print, id); %};
|
%code%{ Slic3r::GUI::set_print_callback_event(print, id); %};
|
||||||
|
|
||||||
void add_expert_mode_part( SV *ui_parent, SV *ui_sizer,
|
void set_model_events_from_perl(Model *model,
|
||||||
Model *model,
|
int event_object_selection_changed,
|
||||||
int event_object_selection_changed,
|
int event_object_settings_changed,
|
||||||
int event_object_settings_changed,
|
int event_remove_object,
|
||||||
int event_remove_object,
|
int event_update_scene)
|
||||||
int event_update_scene)
|
%code%{ Slic3r::GUI::set_model_events_from_perl(*model,
|
||||||
%code%{ Slic3r::GUI::add_expert_mode_part( (wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
|
event_object_selection_changed,
|
||||||
(wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"),
|
event_object_settings_changed,
|
||||||
*model,
|
event_remove_object,
|
||||||
event_object_selection_changed,
|
event_update_scene); %};
|
||||||
event_object_settings_changed,
|
|
||||||
event_remove_object,
|
|
||||||
event_update_scene); %};
|
|
||||||
|
|
||||||
void set_objects_from_perl( SV *ui_parent,
|
void set_objects_from_perl( SV *ui_parent,
|
||||||
SV *frequently_changed_parameters_sizer,
|
SV *frequently_changed_parameters_sizer,
|
||||||
SV *expert_mode_part_sizer,
|
SV *info_sizer,
|
||||||
SV *scrolled_window_sizer,
|
|
||||||
SV *btn_export_gcode,
|
SV *btn_export_gcode,
|
||||||
SV *btn_export_stl,
|
|
||||||
SV *btn_reslice,
|
SV *btn_reslice,
|
||||||
SV *btn_print,
|
SV *btn_print,
|
||||||
SV *btn_send_gcode,
|
SV *btn_send_gcode,
|
||||||
@ -123,10 +118,8 @@ void set_objects_from_perl( SV *ui_parent,
|
|||||||
%code%{ Slic3r::GUI::set_objects_from_perl(
|
%code%{ Slic3r::GUI::set_objects_from_perl(
|
||||||
(wxWindow *)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
|
(wxWindow *)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
|
||||||
(wxBoxSizer *)wxPli_sv_2_object(aTHX_ frequently_changed_parameters_sizer, "Wx::BoxSizer"),
|
(wxBoxSizer *)wxPli_sv_2_object(aTHX_ frequently_changed_parameters_sizer, "Wx::BoxSizer"),
|
||||||
(wxBoxSizer *)wxPli_sv_2_object(aTHX_ expert_mode_part_sizer, "Wx::BoxSizer"),
|
(wxBoxSizer *)wxPli_sv_2_object(aTHX_ info_sizer, "Wx::BoxSizer"),
|
||||||
(wxBoxSizer *)wxPli_sv_2_object(aTHX_ scrolled_window_sizer, "Wx::BoxSizer"),
|
|
||||||
(wxButton *)wxPli_sv_2_object(aTHX_ btn_export_gcode, "Wx::Button"),
|
(wxButton *)wxPli_sv_2_object(aTHX_ btn_export_gcode, "Wx::Button"),
|
||||||
(wxButton *)wxPli_sv_2_object(aTHX_ btn_export_stl, "Wx::Button"),
|
|
||||||
(wxButton *)wxPli_sv_2_object(aTHX_ btn_reslice, "Wx::Button"),
|
(wxButton *)wxPli_sv_2_object(aTHX_ btn_reslice, "Wx::Button"),
|
||||||
(wxButton *)wxPli_sv_2_object(aTHX_ btn_print, "Wx::Button"),
|
(wxButton *)wxPli_sv_2_object(aTHX_ btn_print, "Wx::Button"),
|
||||||
(wxButton *)wxPli_sv_2_object(aTHX_ btn_send_gcode, "Wx::Button"),
|
(wxButton *)wxPli_sv_2_object(aTHX_ btn_send_gcode, "Wx::Button"),
|
||||||
@ -161,6 +154,9 @@ void unselect_objects()
|
|||||||
void select_current_object(int idx)
|
void select_current_object(int idx)
|
||||||
%code%{ Slic3r::GUI::select_current_object(idx); %};
|
%code%{ Slic3r::GUI::select_current_object(idx); %};
|
||||||
|
|
||||||
|
void select_current_volume(int idx, int vol_idx)
|
||||||
|
%code%{ Slic3r::GUI::select_current_volume(idx, vol_idx); %};
|
||||||
|
|
||||||
void remove_obj()
|
void remove_obj()
|
||||||
%code%{ Slic3r::GUI::remove(); %};
|
%code%{ Slic3r::GUI::remove(); %};
|
||||||
|
|
||||||
@ -179,16 +175,28 @@ void desktop_open_datadir_folder()
|
|||||||
void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst)
|
void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst)
|
||||||
%code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %};
|
%code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %};
|
||||||
|
|
||||||
void set_3DScene(SV *scene)
|
|
||||||
%code%{ Slic3r::GUI::set_3DScene((_3DScene *)wxPli_sv_2_object(aTHX_ scene, "Slic3r::Model::3DScene") ); %};
|
|
||||||
|
|
||||||
void register_on_request_update_callback(SV* callback)
|
void register_on_request_update_callback(SV* callback)
|
||||||
%code%{ Slic3r::GUI::g_on_request_update_callback.register_callback(callback); %};
|
%code%{ Slic3r::GUI::g_on_request_update_callback.register_callback(callback); %};
|
||||||
|
|
||||||
void deregister_on_request_update_callback()
|
void deregister_on_request_update_callback()
|
||||||
%code%{ Slic3r::GUI::g_on_request_update_callback.deregister_callback(); %};
|
%code%{ Slic3r::GUI::g_on_request_update_callback.deregister_callback(); %};
|
||||||
|
|
||||||
//void create_double_slider(SV *ui_parent, SV *ui_ds)
|
void create_double_slider(SV *ui_parent, SV *ui_sizer, SV *ui_canvas)
|
||||||
// %code%{ Slic3r::GUI::create_double_slider( (wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
|
%code%{ Slic3r::GUI::create_double_slider( (wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
|
||||||
// (wxControl*)wxPli_sv_2_object(aTHX_ ui_ds, "Wx::Control")); %};
|
(wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"),
|
||||||
|
(wxGLCanvas*)wxPli_sv_2_object(aTHX_ ui_canvas, "Wx::GLCanvas")); %};
|
||||||
|
|
||||||
|
void update_double_slider(bool force_sliders_full_range)
|
||||||
|
%code%{ Slic3r::GUI::update_double_slider(force_sliders_full_range); %};
|
||||||
|
|
||||||
|
void reset_double_slider()
|
||||||
|
%code%{ Slic3r::GUI::reset_double_slider(); %};
|
||||||
|
|
||||||
|
void enable_action_buttons(bool enable)
|
||||||
|
%code%{ Slic3r::GUI::enable_action_buttons(enable); %};
|
||||||
|
|
||||||
|
void save_window_size(SV *window, std::string name)
|
||||||
|
%code%{ Slic3r::GUI::save_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %};
|
||||||
|
|
||||||
|
void restore_window_size(SV *window, std::string name)
|
||||||
|
%code%{ Slic3r::GUI::restore_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %};
|
||||||
|
@ -767,6 +767,15 @@ get_first_volume_id(canvas, obj_idx)
|
|||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
|
||||||
|
int
|
||||||
|
get_in_object_volume_id(canvas, scene_vol_idx)
|
||||||
|
SV *canvas;
|
||||||
|
int scene_vol_idx;
|
||||||
|
CODE:
|
||||||
|
RETVAL = _3DScene::get_in_object_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), scene_vol_idx);
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
std::vector<int>
|
std::vector<int>
|
||||||
load_model(canvas, model, obj_idx)
|
load_model(canvas, model, obj_idx)
|
||||||
SV *canvas;
|
SV *canvas;
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
%code%{ RETVAL = &THIS->thin_fills; %};
|
%code%{ RETVAL = &THIS->thin_fills; %};
|
||||||
Ref<SurfaceCollection> fill_surfaces()
|
Ref<SurfaceCollection> fill_surfaces()
|
||||||
%code%{ RETVAL = &THIS->fill_surfaces; %};
|
%code%{ RETVAL = &THIS->fill_surfaces; %};
|
||||||
Ref<SurfaceCollection> perimeter_surfaces()
|
|
||||||
%code%{ RETVAL = &THIS->perimeter_surfaces; %};
|
|
||||||
Polygons bridged()
|
Polygons bridged()
|
||||||
%code%{ RETVAL = THIS->bridged; %};
|
%code%{ RETVAL = THIS->bridged; %};
|
||||||
Ref<PolylineCollection> unsupported_bridge_edges()
|
Ref<PolylineCollection> unsupported_bridge_edges()
|
||||||
|
@ -340,9 +340,19 @@ ModelMaterial::attributes()
|
|||||||
%code%{ RETVAL = &THIS->mesh; %};
|
%code%{ RETVAL = &THIS->mesh; %};
|
||||||
|
|
||||||
bool modifier()
|
bool modifier()
|
||||||
%code%{ RETVAL = THIS->modifier; %};
|
%code%{ RETVAL = THIS->is_modifier(); %};
|
||||||
void set_modifier(bool modifier)
|
void set_modifier(bool modifier)
|
||||||
%code%{ THIS->modifier = modifier; %};
|
%code%{ THIS->set_type(modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); %};
|
||||||
|
bool model_part()
|
||||||
|
%code%{ RETVAL = THIS->is_model_part(); %};
|
||||||
|
bool support_enforcer()
|
||||||
|
%code%{ RETVAL = THIS->is_support_enforcer(); %};
|
||||||
|
void set_support_enforcer()
|
||||||
|
%code%{ THIS->set_type(ModelVolume::SUPPORT_ENFORCER); %};
|
||||||
|
bool support_blocker()
|
||||||
|
%code%{ RETVAL = THIS->is_support_blocker(); %};
|
||||||
|
void set_support_blocker()
|
||||||
|
%code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %};
|
||||||
|
|
||||||
size_t split(unsigned int max_extruders);
|
size_t split(unsigned int max_extruders);
|
||||||
|
|
||||||
@ -358,15 +368,28 @@ ModelMaterial::attributes()
|
|||||||
%code%{ RETVAL = THIS->rotation; %};
|
%code%{ RETVAL = THIS->rotation; %};
|
||||||
double scaling_factor()
|
double scaling_factor()
|
||||||
%code%{ RETVAL = THIS->scaling_factor; %};
|
%code%{ RETVAL = THIS->scaling_factor; %};
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
Vec2d* offset()
|
||||||
|
%code%{ RETVAL = new Vec2d(THIS->get_offset(X), THIS->get_offset(Y)); %};
|
||||||
|
#else
|
||||||
Ref<Vec2d> offset()
|
Ref<Vec2d> offset()
|
||||||
%code%{ RETVAL = &THIS->offset; %};
|
%code%{ RETVAL = &THIS->offset; %};
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
|
||||||
void set_rotation(double val)
|
void set_rotation(double val)
|
||||||
%code%{ THIS->rotation = val; THIS->get_object()->invalidate_bounding_box(); %};
|
%code%{ THIS->rotation = val; THIS->get_object()->invalidate_bounding_box(); %};
|
||||||
void set_scaling_factor(double val)
|
void set_scaling_factor(double val)
|
||||||
%code%{ THIS->scaling_factor = val; THIS->get_object()->invalidate_bounding_box(); %};
|
%code%{ THIS->scaling_factor = val; THIS->get_object()->invalidate_bounding_box(); %};
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
void set_offset(Vec2d *offset)
|
||||||
|
%code%{
|
||||||
|
THIS->set_offset(X, (*offset)(0));
|
||||||
|
THIS->set_offset(Y, (*offset)(1));
|
||||||
|
%};
|
||||||
|
#else
|
||||||
void set_offset(Vec2d *offset)
|
void set_offset(Vec2d *offset)
|
||||||
%code%{ THIS->offset = *offset; %};
|
%code%{ THIS->offset = *offset; %};
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
|
||||||
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
|
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
|
||||||
void transform_polygon(Polygon* polygon) const;
|
void transform_polygon(Polygon* polygon) const;
|
||||||
|
@ -103,6 +103,12 @@ _constant()
|
|||||||
%code%{ RETVAL = THIS->print_statistics().total_weight; %};
|
%code%{ RETVAL = THIS->print_statistics().total_weight; %};
|
||||||
double total_cost()
|
double total_cost()
|
||||||
%code%{ RETVAL = THIS->print_statistics().total_cost; %};
|
%code%{ RETVAL = THIS->print_statistics().total_cost; %};
|
||||||
|
double total_wipe_tower_cost()
|
||||||
|
%code%{ RETVAL = THIS->print_statistics().total_wipe_tower_cost; %};
|
||||||
|
double total_wipe_tower_filament()
|
||||||
|
%code%{ RETVAL = THIS->print_statistics().total_wipe_tower_filament; %};
|
||||||
|
int wipe_tower_number_of_toolchanges()
|
||||||
|
%code%{ RETVAL = THIS->wipe_tower_data().number_of_toolchanges; %};
|
||||||
PrintObjectPtrs* objects()
|
PrintObjectPtrs* objects()
|
||||||
%code%{ RETVAL = const_cast<PrintObjectPtrs*>(&THIS->objects()); %};
|
%code%{ RETVAL = const_cast<PrintObjectPtrs*>(&THIS->objects()); %};
|
||||||
void clear_objects();
|
void clear_objects();
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
%{
|
%{
|
||||||
#include <xsinit.h>
|
#include <xsinit.h>
|
||||||
#include "slic3r/GUI/ProgressStatusBar.hpp"
|
#include "slic3r/GUI/ProgressStatusBar.hpp"
|
||||||
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%name{Slic3r::GUI::ProgressStatusBar} class ProgressStatusBar {
|
%name{Slic3r::GUI::ProgressStatusBar} class ProgressStatusBar {
|
||||||
@ -37,7 +38,7 @@
|
|||||||
%code%{ THIS->embed(); %};
|
%code%{ THIS->embed(); %};
|
||||||
|
|
||||||
void SetStatusText(const char *txt)
|
void SetStatusText(const char *txt)
|
||||||
%code%{ THIS->set_status_text(txt); %};
|
%code%{ THIS->set_status_text(_(txt)); %};
|
||||||
|
|
||||||
void SetCancelCallback(SV* callback)
|
void SetCancelCallback(SV* callback)
|
||||||
%code%{ THIS->m_perl_cancel_callback.register_callback(callback); THIS->show_cancel_button();%};
|
%code%{ THIS->m_perl_cancel_callback.register_callback(callback); THIS->show_cancel_button();%};
|
||||||
|
@ -48,6 +48,11 @@ trace(level, message)
|
|||||||
CODE:
|
CODE:
|
||||||
Slic3r::trace(level, message);
|
Slic3r::trace(level, message);
|
||||||
|
|
||||||
|
void
|
||||||
|
disable_multi_threading()
|
||||||
|
CODE:
|
||||||
|
Slic3r::disable_multi_threading();
|
||||||
|
|
||||||
void
|
void
|
||||||
set_var_dir(dir)
|
set_var_dir(dir)
|
||||||
char *dir;
|
char *dir;
|
||||||
|
Loading…
Reference in New Issue
Block a user