mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-12-10 15:28:45 -07:00
Merge branch 'master' into tm_colldetection_upgr
This commit is contained in:
commit
03079381e1
119 changed files with 11942 additions and 8619 deletions
|
|
@ -1,337 +0,0 @@
|
|||
package Slic3r::GUI;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use File::Basename qw(basename);
|
||||
use FindBin;
|
||||
use List::Util qw(first);
|
||||
use Slic3r::GUI::MainFrame;
|
||||
use Slic3r::GUI::Plater;
|
||||
use Slic3r::GUI::Plater::3D;
|
||||
use Slic3r::GUI::Plater::3DPreview;
|
||||
|
||||
use Wx::Locale gettext => 'L';
|
||||
|
||||
our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1";
|
||||
|
||||
use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow :filedialog :font);
|
||||
use Wx::Event qw(EVT_IDLE EVT_COMMAND EVT_MENU);
|
||||
use base 'Wx::App';
|
||||
|
||||
use constant FILE_WILDCARDS => {
|
||||
known => 'Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.zip.amf;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA',
|
||||
stl => 'STL files (*.stl)|*.stl;*.STL',
|
||||
obj => 'OBJ files (*.obj)|*.obj;*.OBJ',
|
||||
amf => 'AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML',
|
||||
threemf => '3MF files (*.3mf)|*.3mf;*.3MF',
|
||||
prusa => 'Prusa Control files (*.prusa)|*.prusa;*.PRUSA',
|
||||
ini => 'INI files *.ini|*.ini;*.INI',
|
||||
gcode => 'G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC',
|
||||
};
|
||||
use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf threemf prusa)};
|
||||
|
||||
# Datadir provided on the command line.
|
||||
our $datadir;
|
||||
our $no_plater;
|
||||
our @cb;
|
||||
|
||||
our $small_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
$small_font->SetPointSize(11) if &Wx::wxMAC;
|
||||
our $small_bold_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
$small_bold_font->SetPointSize(11) if &Wx::wxMAC;
|
||||
$small_bold_font->SetWeight(wxFONTWEIGHT_BOLD);
|
||||
our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
$medium_font->SetPointSize(12);
|
||||
our $grey = Wx::Colour->new(200,200,200);
|
||||
|
||||
# Events to be sent from a C++ menu implementation:
|
||||
# 1) To inform about a change of the application language.
|
||||
our $LANGUAGE_CHANGE_EVENT = Wx::NewEventType;
|
||||
# 2) To inform about a change of Preferences.
|
||||
our $PREFERENCES_EVENT = Wx::NewEventType;
|
||||
# To inform AppConfig about Slic3r version available online
|
||||
our $VERSION_ONLINE_EVENT = Wx::NewEventType;
|
||||
|
||||
sub OnInit {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->SetAppName('Slic3rPE');
|
||||
$self->SetAppDisplayName('Slic3r Prusa Edition');
|
||||
Slic3r::debugf "wxWidgets version %s, Wx version %s\n", &Wx::wxVERSION_STRING, $Wx::VERSION;
|
||||
|
||||
# Set the Slic3r data directory at the Slic3r XS module.
|
||||
# Unix: ~/.Slic3r
|
||||
# Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r"
|
||||
# Mac: "~/Library/Application Support/Slic3r"
|
||||
Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir);
|
||||
Slic3r::GUI::set_wxapp($self);
|
||||
|
||||
$self->{app_config} = Slic3r::GUI::AppConfig->new;
|
||||
Slic3r::GUI::set_app_config($self->{app_config});
|
||||
$self->{preset_bundle} = Slic3r::GUI::PresetBundle->new;
|
||||
Slic3r::GUI::set_preset_bundle($self->{preset_bundle});
|
||||
|
||||
# just checking for existence of Slic3r::data_dir is not enough: it may be an empty directory
|
||||
# supplied as argument to --datadir; in that case we should still run the wizard
|
||||
eval { $self->{preset_bundle}->setup_directories() };
|
||||
if ($@) {
|
||||
warn $@ . "\n";
|
||||
fatal_error(undef, $@);
|
||||
}
|
||||
my $app_conf_exists = $self->{app_config}->exists;
|
||||
# load settings
|
||||
$self->{app_config}->load if $app_conf_exists;
|
||||
$self->{app_config}->set('version', $Slic3r::VERSION);
|
||||
$self->{app_config}->save;
|
||||
|
||||
$self->{preset_updater} = Slic3r::PresetUpdater->new($VERSION_ONLINE_EVENT);
|
||||
Slic3r::GUI::set_preset_updater($self->{preset_updater});
|
||||
|
||||
Slic3r::GUI::load_language();
|
||||
|
||||
# Suppress the '- default -' presets.
|
||||
$self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0);
|
||||
eval { $self->{preset_bundle}->load_presets($self->{app_config}); };
|
||||
if ($@) {
|
||||
warn $@ . "\n";
|
||||
show_error(undef, $@);
|
||||
}
|
||||
|
||||
# application frame
|
||||
print STDERR "Creating main frame...\n";
|
||||
Wx::Image::FindHandlerType(wxBITMAP_TYPE_PNG) || Wx::Image::AddHandler(Wx::PNGHandler->new);
|
||||
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
|
||||
no_plater => $no_plater,
|
||||
lang_ch_event => $LANGUAGE_CHANGE_EVENT,
|
||||
preferences_event => $PREFERENCES_EVENT,
|
||||
);
|
||||
$self->SetTopWindow($frame);
|
||||
|
||||
# This makes CallAfter() work
|
||||
EVT_IDLE($self->{mainframe}, sub {
|
||||
while (my $cb = shift @cb) {
|
||||
$cb->();
|
||||
}
|
||||
$self->{app_config}->save if $self->{app_config}->dirty;
|
||||
});
|
||||
|
||||
# On OS X the UI tends to freeze in weird ways if modal dialogs (config wizard, update notifications, ...)
|
||||
# are shown before or in the same event callback with the main frame creation.
|
||||
# Therefore we schedule them for later using CallAfter.
|
||||
$self->CallAfter(sub {
|
||||
eval {
|
||||
if (! $self->{preset_updater}->config_update()) {
|
||||
$self->{mainframe}->Close;
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
show_error(undef, $@);
|
||||
$self->{mainframe}->Close;
|
||||
}
|
||||
});
|
||||
|
||||
$self->CallAfter(sub {
|
||||
if (! Slic3r::GUI::config_wizard_startup($app_conf_exists)) {
|
||||
# Only notify if there was not wizard so as not to bother too much ...
|
||||
$self->{preset_updater}->slic3r_update_notify();
|
||||
}
|
||||
$self->{preset_updater}->sync($self->{preset_bundle});
|
||||
});
|
||||
|
||||
# The following event is emited by the C++ menu implementation of application language change.
|
||||
EVT_COMMAND($self, -1, $LANGUAGE_CHANGE_EVENT, sub{
|
||||
print STDERR "LANGUAGE_CHANGE_EVENT\n";
|
||||
$self->recreate_GUI;
|
||||
});
|
||||
|
||||
# The following event is emited by the C++ menu implementation of preferences change.
|
||||
EVT_COMMAND($self, -1, $PREFERENCES_EVENT, sub{
|
||||
$self->update_ui_from_settings;
|
||||
});
|
||||
|
||||
# The following event is emited by PresetUpdater (C++) to inform about
|
||||
# the newer Slic3r application version avaiable online.
|
||||
EVT_COMMAND($self, -1, $VERSION_ONLINE_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $version = $event->GetString;
|
||||
$self->{app_config}->set('version_online', $version);
|
||||
$self->{app_config}->save;
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub recreate_GUI{
|
||||
print STDERR "recreate_GUI\n";
|
||||
my ($self) = @_;
|
||||
my $topwindow = $self->GetTopWindow();
|
||||
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
|
||||
no_plater => $no_plater,
|
||||
lang_ch_event => $LANGUAGE_CHANGE_EVENT,
|
||||
preferences_event => $PREFERENCES_EVENT,
|
||||
);
|
||||
|
||||
if($topwindow)
|
||||
{
|
||||
$self->SetTopWindow($frame);
|
||||
$topwindow->Destroy;
|
||||
}
|
||||
|
||||
EVT_IDLE($self->{mainframe}, sub {
|
||||
while (my $cb = shift @cb) {
|
||||
$cb->();
|
||||
}
|
||||
$self->{app_config}->save if $self->{app_config}->dirty;
|
||||
});
|
||||
|
||||
# On OSX the UI was not initialized correctly if the wizard was called
|
||||
# before the UI was up and running.
|
||||
$self->CallAfter(sub {
|
||||
# Run the config wizard, don't offer the "reset user profile" checkbox.
|
||||
Slic3r::GUI::config_wizard_startup(1);
|
||||
});
|
||||
}
|
||||
|
||||
sub system_info {
|
||||
my ($self) = @_;
|
||||
my $slic3r_info = Slic3r::slic3r_info(format => 'html');
|
||||
my $copyright_info = Slic3r::copyright_info(format => 'html');
|
||||
my $system_info = Slic3r::system_info(format => 'html');
|
||||
my $opengl_info;
|
||||
my $opengl_info_txt = '';
|
||||
if (defined($self->{mainframe}) && defined($self->{mainframe}->{plater}) &&
|
||||
defined($self->{mainframe}->{plater}->{canvas3D})) {
|
||||
$opengl_info = Slic3r::GUI::_3DScene::get_gl_info(1, 1);
|
||||
$opengl_info_txt = Slic3r::GUI::_3DScene::get_gl_info(0, 1);
|
||||
}
|
||||
# my $about = Slic3r::GUI::SystemInfo->new(
|
||||
# parent => undef,
|
||||
# slic3r_info => $slic3r_info,
|
||||
# system_info => $system_info,
|
||||
# opengl_info => $opengl_info,
|
||||
# text_info => Slic3r::slic3r_info . Slic3r::system_info . $opengl_info_txt,
|
||||
# );
|
||||
# $about->ShowModal;
|
||||
# $about->Destroy;
|
||||
}
|
||||
|
||||
# static method accepting a wxWindow object as first parameter
|
||||
sub catch_error {
|
||||
my ($self, $cb, $message_dialog) = @_;
|
||||
if (my $err = $@) {
|
||||
$cb->() if $cb;
|
||||
$message_dialog
|
||||
? $message_dialog->($err, 'Error', wxOK | wxICON_ERROR)
|
||||
: Slic3r::GUI::show_error($self, $err);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# static method accepting a wxWindow object as first parameter
|
||||
sub show_error {
|
||||
my ($parent, $message) = @_;
|
||||
Slic3r::GUI::show_error_id($parent ? $parent->GetId() : 0, $message);
|
||||
}
|
||||
|
||||
# static method accepting a wxWindow object as first parameter
|
||||
sub show_info {
|
||||
my ($parent, $message, $title) = @_;
|
||||
Wx::MessageDialog->new($parent, $message, $title || 'Notice', wxOK | wxICON_INFORMATION)->ShowModal;
|
||||
}
|
||||
|
||||
# static method accepting a wxWindow object as first parameter
|
||||
sub fatal_error {
|
||||
show_error(@_);
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# static method accepting a wxWindow object as first parameter
|
||||
sub warning_catcher {
|
||||
my ($self, $message_dialog) = @_;
|
||||
return sub {
|
||||
my $message = shift;
|
||||
return if $message =~ /GLUquadricObjPtr|Attempt to free unreferenced scalar/;
|
||||
my @params = ($message, 'Warning', wxOK | wxICON_WARNING);
|
||||
$message_dialog
|
||||
? $message_dialog->(@params)
|
||||
: Wx::MessageDialog->new($self, @params)->ShowModal;
|
||||
};
|
||||
}
|
||||
|
||||
sub notify {
|
||||
my ($self, $message) = @_;
|
||||
|
||||
my $frame = $self->GetTopWindow;
|
||||
# try harder to attract user attention on OS X
|
||||
$frame->RequestUserAttention(&Wx::wxMAC ? wxUSER_ATTENTION_ERROR : wxUSER_ATTENTION_INFO)
|
||||
unless ($frame->IsActive);
|
||||
|
||||
# There used to be notifier using a Growl application for OSX, but Growl is dead.
|
||||
# The notifier also supported the Linux X D-bus notifications, but that support was broken.
|
||||
#TODO use wxNotificationMessage?
|
||||
}
|
||||
|
||||
# Called after the Preferences dialog is closed and the program settings are saved.
|
||||
# Update the UI based on the current preferences.
|
||||
sub update_ui_from_settings {
|
||||
my ($self) = @_;
|
||||
$self->{mainframe}->update_ui_from_settings;
|
||||
}
|
||||
|
||||
sub open_model {
|
||||
my ($self, $window) = @_;
|
||||
|
||||
my $dlg_title = L('Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):');
|
||||
my $dialog = Wx::FileDialog->new($window // $self->GetTopWindow, $dlg_title,
|
||||
$self->{app_config}->get_last_dir, "",
|
||||
MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
|
||||
if ($dialog->ShowModal != wxID_OK) {
|
||||
$dialog->Destroy;
|
||||
return;
|
||||
}
|
||||
my @input_files = $dialog->GetPaths;
|
||||
$dialog->Destroy;
|
||||
return @input_files;
|
||||
}
|
||||
|
||||
sub CallAfter {
|
||||
my ($self, $cb) = @_;
|
||||
push @cb, $cb;
|
||||
}
|
||||
|
||||
sub append_menu_item {
|
||||
my ($self, $menu, $string, $description, $cb, $id, $icon, $kind) = @_;
|
||||
|
||||
$id //= &Wx::NewId();
|
||||
my $item = Wx::MenuItem->new($menu, $id, $string, $description // '', $kind // 0);
|
||||
$self->set_menu_item_icon($item, $icon);
|
||||
$menu->Append($item);
|
||||
|
||||
EVT_MENU($self, $id, $cb);
|
||||
return $item;
|
||||
}
|
||||
|
||||
sub append_submenu {
|
||||
my ($self, $menu, $string, $description, $submenu, $id, $icon) = @_;
|
||||
|
||||
$id //= &Wx::NewId();
|
||||
my $item = Wx::MenuItem->new($menu, $id, $string, $description // '');
|
||||
$self->set_menu_item_icon($item, $icon);
|
||||
$item->SetSubMenu($submenu);
|
||||
$menu->Append($item);
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
sub set_menu_item_icon {
|
||||
my ($self, $menuItem, $icon) = @_;
|
||||
|
||||
# SetBitmap was not available on OS X before Wx 0.9927
|
||||
if ($icon && $menuItem->can('SetBitmap')) {
|
||||
$menuItem->SetBitmap(Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG));
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
# Implements pure perl packages
|
||||
#
|
||||
# Slic3r::GUI::3DScene::Base;
|
||||
# Slic3r::GUI::3DScene;
|
||||
#
|
||||
# Slic3r::GUI::Plater::3D derives from Slic3r::GUI::3DScene,
|
||||
# Slic3r::GUI::Plater::3DPreview,
|
||||
# Slic3r::GUI::Plater::ObjectCutDialog and Slic3r::GUI::Plater::ObjectPartsPanel
|
||||
# own $self->{canvas} of the Slic3r::GUI::3DScene type.
|
||||
#
|
||||
# Therefore the 3DScene supports renderng of STLs, extrusions and cutting planes,
|
||||
# and camera manipulation.
|
||||
|
||||
package Slic3r::GUI::3DScene::Base;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Wx qw(wxTheApp :timer :bitmap :icon :dialog);
|
||||
# must load OpenGL *before* Wx::GLCanvas
|
||||
use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
|
||||
use base qw(Wx::GLCanvas Class::Accessor);
|
||||
use Wx::GLCanvas qw(:all);
|
||||
|
||||
sub new {
|
||||
my ($class, $parent) = @_;
|
||||
|
||||
# We can only enable multi sample anti aliasing wih wxWidgets 3.0.3 and with a hacked Wx::GLCanvas,
|
||||
# which exports some new WX_GL_XXX constants, namely WX_GL_SAMPLE_BUFFERS and WX_GL_SAMPLES.
|
||||
my $can_multisample =
|
||||
! wxTheApp->{app_config}->get('use_legacy_opengl') &&
|
||||
Wx::wxVERSION >= 3.000003 &&
|
||||
defined Wx::GLCanvas->can('WX_GL_SAMPLE_BUFFERS') &&
|
||||
defined Wx::GLCanvas->can('WX_GL_SAMPLES');
|
||||
my $attrib = [WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24];
|
||||
if ($can_multisample) {
|
||||
# Request a window with multi sampled anti aliasing. This is a new feature in Wx 3.0.3 (backported from 3.1.0).
|
||||
# Use eval to avoid compilation, if the subs WX_GL_SAMPLE_BUFFERS and WX_GL_SAMPLES are missing.
|
||||
eval 'push(@$attrib, (WX_GL_SAMPLE_BUFFERS, 1, WX_GL_SAMPLES, 4));';
|
||||
}
|
||||
# wxWidgets expect the attrib list to be ended by zero.
|
||||
push(@$attrib, 0);
|
||||
|
||||
# we request a depth buffer explicitely because it looks like it's not created by
|
||||
# default on Linux, causing transparency issues
|
||||
my $self = $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "", $attrib);
|
||||
|
||||
Slic3r::GUI::_3DScene::add_canvas($self);
|
||||
Slic3r::GUI::_3DScene::allow_multisample($self, $can_multisample);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub Destroy {
|
||||
my ($self) = @_;
|
||||
Slic3r::GUI::_3DScene::remove_canvas($self);
|
||||
return $self->SUPER::Destroy;
|
||||
}
|
||||
|
||||
# The 3D canvas to display objects and tool paths.
|
||||
package Slic3r::GUI::3DScene;
|
||||
use base qw(Slic3r::GUI::3DScene::Base);
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
|
||||
my $self = $class->SUPER::new(@_);
|
||||
return $self;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -1,644 +0,0 @@
|
|||
# The main frame, the parent of all.
|
||||
|
||||
package Slic3r::GUI::MainFrame;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use File::Basename qw(basename dirname);
|
||||
use FindBin;
|
||||
use List::Util qw(min first);
|
||||
use Slic3r::Geometry qw(X Y);
|
||||
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog :dirdialog
|
||||
:font :icon wxTheApp);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_COMMAND EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED);
|
||||
use base 'Wx::Frame';
|
||||
|
||||
use Wx::Locale gettext => 'L';
|
||||
|
||||
our $qs_last_input_file;
|
||||
our $qs_last_output_file;
|
||||
our $last_config;
|
||||
our $appController;
|
||||
|
||||
# Events to be sent from a C++ Tab implementation:
|
||||
# 1) To inform about a change of a configuration value.
|
||||
our $VALUE_CHANGE_EVENT = Wx::NewEventType;
|
||||
# 2) To inform about a preset selection change or a "modified" status change.
|
||||
our $PRESETS_CHANGED_EVENT = Wx::NewEventType;
|
||||
# 3) To update the status bar with the progress information.
|
||||
our $PROGRESS_BAR_EVENT = Wx::NewEventType;
|
||||
# 4) To display an error dialog box from a thread on the UI thread.
|
||||
our $ERROR_EVENT = Wx::NewEventType;
|
||||
# 5) To inform about a change of object selection
|
||||
our $OBJECT_SELECTION_CHANGED_EVENT = Wx::NewEventType;
|
||||
# 6) To inform about a change of object settings
|
||||
our $OBJECT_SETTINGS_CHANGED_EVENT = Wx::NewEventType;
|
||||
# 7) To inform about a remove of object
|
||||
our $OBJECT_REMOVE_EVENT = Wx::NewEventType;
|
||||
# 8) To inform about a update of the scene
|
||||
our $UPDATE_SCENE_EVENT = Wx::NewEventType;
|
||||
|
||||
sub new {
|
||||
my ($class, %params) = @_;
|
||||
|
||||
my $self = $class->SUPER::new(undef, -1, $Slic3r::FORK_NAME . ' - ' . $Slic3r::VERSION, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE);
|
||||
Slic3r::GUI::set_main_frame($self);
|
||||
|
||||
$appController = Slic3r::AppController->new();
|
||||
|
||||
if ($^O eq 'MSWin32') {
|
||||
# Load the icon either from the exe, or from the ico file.
|
||||
my $iconfile = Slic3r::decode_path($FindBin::Bin) . '\slic3r.exe';
|
||||
$iconfile = Slic3r::var("Slic3r.ico") unless -f $iconfile;
|
||||
$self->SetIcon(Wx::Icon->new($iconfile, wxBITMAP_TYPE_ICO));
|
||||
} else {
|
||||
$self->SetIcon(Wx::Icon->new(Slic3r::var("Slic3r_128px.png"), wxBITMAP_TYPE_PNG));
|
||||
}
|
||||
|
||||
# store input params
|
||||
$self->{no_plater} = $params{no_plater};
|
||||
$self->{loaded} = 0;
|
||||
$self->{lang_ch_event} = $params{lang_ch_event};
|
||||
$self->{preferences_event} = $params{preferences_event};
|
||||
|
||||
# initialize tabpanel and menubar
|
||||
$self->_init_tabpanel;
|
||||
$self->_init_menubar;
|
||||
|
||||
# set default tooltip timer in msec
|
||||
# SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values
|
||||
# (SetAutoPop is not available on GTK.)
|
||||
eval { Wx::ToolTip::SetAutoPop(32767) };
|
||||
|
||||
# initialize status bar
|
||||
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new();
|
||||
$self->{statusbar}->Embed;
|
||||
$self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
|
||||
# Make the global status bar and its progress indicator available in C++
|
||||
Slic3r::GUI::set_progress_status_bar($self->{statusbar});
|
||||
$appController->set_global_progress_indicator($self->{statusbar});
|
||||
|
||||
$appController->set_model($self->{plater}->{model});
|
||||
$appController->set_print($self->{plater}->{print});
|
||||
|
||||
$self->{plater}->{appController} = $appController;
|
||||
|
||||
$self->{loaded} = 1;
|
||||
|
||||
# initialize layout
|
||||
{
|
||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$sizer->Add($self->{tabpanel}, 1, wxEXPAND);
|
||||
$sizer->SetSizeHints($self);
|
||||
$self->SetSizer($sizer);
|
||||
$self->Fit;
|
||||
$self->SetMinSize([760, 490]);
|
||||
$self->SetSize($self->GetMinSize);
|
||||
Slic3r::GUI::restore_window_size($self, "main_frame");
|
||||
$self->Show;
|
||||
$self->Layout;
|
||||
}
|
||||
|
||||
# declare events
|
||||
EVT_CLOSE($self, sub {
|
||||
my (undef, $event) = @_;
|
||||
if ($event->CanVeto && !Slic3r::GUI::check_unsaved_changes) {
|
||||
$event->Veto;
|
||||
return;
|
||||
}
|
||||
# save window size
|
||||
Slic3r::GUI::save_window_size($self, "main_frame");
|
||||
# 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.
|
||||
wxTheApp->{app_config}->save;
|
||||
$self->{statusbar}->ResetCancelCallback();
|
||||
$self->{plater}->{print} = undef if($self->{plater});
|
||||
Slic3r::GUI::_3DScene::remove_all_canvases();
|
||||
Slic3r::GUI::deregister_on_request_update_callback();
|
||||
# propagate event
|
||||
$event->Skip;
|
||||
});
|
||||
|
||||
$self->update_ui_from_settings;
|
||||
|
||||
Slic3r::GUI::update_mode();
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub _init_tabpanel {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->{tabpanel} = my $panel = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
|
||||
Slic3r::GUI::set_tab_panel($panel);
|
||||
|
||||
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{tabpanel}, sub {
|
||||
my $panel = $self->{tabpanel}->GetCurrentPage;
|
||||
$panel->OnActivate if $panel->can('OnActivate');
|
||||
|
||||
for my $tab_name (qw(print filament printer)) {
|
||||
Slic3r::GUI::get_preset_tab("$tab_name")->OnActivate if ("$tab_name" eq $panel->GetName);
|
||||
}
|
||||
});
|
||||
|
||||
if (!$self->{no_plater}) {
|
||||
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel,
|
||||
event_object_selection_changed => $OBJECT_SELECTION_CHANGED_EVENT,
|
||||
event_object_settings_changed => $OBJECT_SETTINGS_CHANGED_EVENT,
|
||||
event_remove_object => $OBJECT_REMOVE_EVENT,
|
||||
event_update_scene => $UPDATE_SCENE_EVENT,
|
||||
), L("Plater"));
|
||||
}
|
||||
|
||||
#TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view.
|
||||
# The following event is emited by the C++ Tab implementation on config value change.
|
||||
EVT_COMMAND($self, -1, $VALUE_CHANGE_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $str = $event->GetString;
|
||||
my ($opt_key, $name) = ($str =~ /(.*) (.*)/);
|
||||
#print "VALUE_CHANGE_EVENT: ", $opt_key, "\n";
|
||||
my $tab = Slic3r::GUI::get_preset_tab($name);
|
||||
my $config = $tab->get_config;
|
||||
if ($self->{plater}) {
|
||||
$self->{plater}->on_config_change($config); # propagate config change events to the plater
|
||||
if ($opt_key eq 'extruders_count'){
|
||||
my $value = $event->GetInt();
|
||||
$self->{plater}->on_extruders_change($value);
|
||||
}
|
||||
if ($opt_key eq 'printer_technology'){
|
||||
my $value = $event->GetInt();# 0 ~ "ptFFF"; 1 ~ "ptSLA"
|
||||
$self->{plater}->show_preset_comboboxes($value);
|
||||
}
|
||||
}
|
||||
# don't save while loading for the first time
|
||||
$self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave && $self->{loaded};
|
||||
});
|
||||
# The following event is emited by the C++ Tab implementation on preset selection,
|
||||
# or when the preset's "modified" status changes.
|
||||
EVT_COMMAND($self, -1, $PRESETS_CHANGED_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $tab_name = $event->GetString;
|
||||
|
||||
my $tab = Slic3r::GUI::get_preset_tab($tab_name);
|
||||
if ($self->{plater}) {
|
||||
# Update preset combo boxes (Print settings, Filament, Material, Printer) from their respective tabs.
|
||||
my $presets = $tab->get_presets;
|
||||
if (defined $presets){
|
||||
my $reload_dependent_tabs = $tab->get_dependent_tabs;
|
||||
$self->{plater}->update_presets($tab_name, $reload_dependent_tabs, $presets);
|
||||
$self->{plater}->{"selected_item_$tab_name"} = $tab->get_selected_preset_item;
|
||||
if ($tab_name eq 'printer') {
|
||||
# Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
|
||||
for my $tab_name_other (qw(print filament sla_material)) {
|
||||
# If the printer tells us that the print or filament preset has been switched or invalidated,
|
||||
# refresh the print or filament tab page. Otherwise just refresh the combo box.
|
||||
my $update_action = ($reload_dependent_tabs && (first { $_ eq $tab_name_other } (@{$reload_dependent_tabs})))
|
||||
? 'load_current_preset' : 'update_tab_ui';
|
||||
$self->{options_tabs}{$tab_name_other}->$update_action;
|
||||
}
|
||||
}
|
||||
$self->{plater}->on_config_change($tab->get_config);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
# The following event is emited by the C++ Tab implementation on object selection change.
|
||||
EVT_COMMAND($self, -1, $OBJECT_SELECTION_CHANGED_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $obj_idx = $event->GetId;
|
||||
# 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);
|
||||
|
||||
my $vol_idx = $event->GetInt;
|
||||
$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.
|
||||
EVT_COMMAND($self, -1, $OBJECT_SETTINGS_CHANGED_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
my $line = $event->GetString;
|
||||
my ($obj_idx, $parts_changed, $part_settings_changed) = split('',$line);
|
||||
|
||||
$self->{plater}->changed_object_settings($obj_idx, $parts_changed, $part_settings_changed);
|
||||
});
|
||||
|
||||
# The following event is emited by the C++ GUI implementation on object remove.
|
||||
EVT_COMMAND($self, -1, $OBJECT_REMOVE_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
$self->{plater}->remove();
|
||||
});
|
||||
|
||||
# The following event is emited by the C++ GUI implementation on extruder change for object.
|
||||
EVT_COMMAND($self, -1, $UPDATE_SCENE_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
$self->{plater}->update();
|
||||
});
|
||||
|
||||
Slic3r::GUI::create_preset_tabs($VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT);
|
||||
$self->{options_tabs} = {};
|
||||
for my $tab_name (qw(print filament sla_material printer)) {
|
||||
$self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name");
|
||||
}
|
||||
|
||||
# Update progress bar with an event sent by the slicing core.
|
||||
EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
if (defined $self->{progress_dialog}) {
|
||||
# If a progress dialog is open, update it.
|
||||
$self->{progress_dialog}->Update($event->GetInt, $event->GetString . "…");
|
||||
} else {
|
||||
# Otherwise update the main window status bar.
|
||||
$self->{statusbar}->SetProgress($event->GetInt);
|
||||
$self->{statusbar}->SetStatusText($event->GetString . "…");
|
||||
}
|
||||
});
|
||||
|
||||
EVT_COMMAND($self, -1, $ERROR_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
Slic3r::GUI::show_error($self, $event->GetString);
|
||||
});
|
||||
|
||||
if ($self->{plater}) {
|
||||
$self->{plater}->on_select_preset(sub {
|
||||
my ($group, $name) = @_;
|
||||
$self->{options_tabs}{$group}->select_preset($name);
|
||||
});
|
||||
# load initial config
|
||||
my $full_config = wxTheApp->{preset_bundle}->full_config;
|
||||
$self->{plater}->on_config_change($full_config);
|
||||
|
||||
# Show a correct number of filament fields.
|
||||
if (defined $full_config->nozzle_diameter){ # nozzle_diameter is undefined when SLA printer is selected
|
||||
$self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter}));
|
||||
}
|
||||
|
||||
# Show correct preset comboboxes according to the printer_technology
|
||||
$self->{plater}->show_preset_comboboxes(($full_config->printer_technology eq "FFF") ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
sub _init_menubar {
|
||||
my ($self) = @_;
|
||||
|
||||
# File menu
|
||||
my $fileMenu = Wx::Menu->new;
|
||||
{
|
||||
wxTheApp->append_menu_item($fileMenu, L("Open STL/OBJ/AMF/3MF…\tCtrl+O"), L('Open a model'), sub {
|
||||
$self->{plater}->add if $self->{plater};
|
||||
}, undef, undef); #'brick_add.png');
|
||||
$self->_append_menu_item($fileMenu, L("&Load Config…\tCtrl+L"), L('Load exported configuration file'), sub {
|
||||
$self->load_config_file;
|
||||
}, undef, 'plugin_add.png');
|
||||
$self->_append_menu_item($fileMenu, L("&Export Config…\tCtrl+E"), L('Export current configuration to file'), sub {
|
||||
$self->export_config;
|
||||
}, undef, 'plugin_go.png');
|
||||
$self->_append_menu_item($fileMenu, L("&Load Config Bundle…"), L('Load presets from a bundle'), sub {
|
||||
$self->load_configbundle;
|
||||
}, undef, 'lorry_add.png');
|
||||
$self->_append_menu_item($fileMenu, L("&Export Config Bundle…"), L('Export all presets to file'), sub {
|
||||
$self->export_configbundle;
|
||||
}, undef, 'lorry_go.png');
|
||||
$fileMenu->AppendSeparator();
|
||||
$self->_append_menu_item($fileMenu, L("Slice to PNG…"), L('Slice file to a set of PNG files'), sub {
|
||||
$self->slice_to_png;
|
||||
}, undef, 'shape_handles.png');
|
||||
$self->{menu_item_reslice_now} = $self->_append_menu_item(
|
||||
$fileMenu, L("(&Re)Slice Now\tCtrl+S"), L('Start new slicing process'),
|
||||
sub { $self->reslice_now; }, undef, 'shape_handles.png');
|
||||
$fileMenu->AppendSeparator();
|
||||
$self->_append_menu_item($fileMenu, L("Repair STL file…"), L('Automatically repair an STL file'), sub {
|
||||
$self->repair_stl;
|
||||
}, undef, 'wrench.png');
|
||||
$fileMenu->AppendSeparator();
|
||||
$self->_append_menu_item($fileMenu, L("&Quit"), L('Quit Slic3r'), sub {
|
||||
$self->Close(0);
|
||||
}, wxID_EXIT);
|
||||
}
|
||||
|
||||
# Plater menu
|
||||
unless ($self->{no_plater}) {
|
||||
my $plater = $self->{plater};
|
||||
|
||||
$self->{plater_menu} = Wx::Menu->new;
|
||||
$self->_append_menu_item($self->{plater_menu}, L("Export G-code..."), L('Export current plate as G-code'), sub {
|
||||
$plater->export_gcode;
|
||||
}, undef, 'cog_go.png');
|
||||
$self->_append_menu_item($self->{plater_menu}, L("Export plate as STL..."), L('Export current plate as STL'), sub {
|
||||
$plater->export_stl;
|
||||
}, undef, 'brick_go.png');
|
||||
$self->_append_menu_item($self->{plater_menu}, L("Export plate as AMF..."), L('Export current plate as AMF'), sub {
|
||||
$plater->export_amf;
|
||||
}, undef, 'brick_go.png');
|
||||
$self->_append_menu_item($self->{plater_menu}, L("Export plate as 3MF..."), L('Export current plate as 3MF'), sub {
|
||||
$plater->export_3mf;
|
||||
}, undef, 'brick_go.png');
|
||||
|
||||
$self->{object_menu} = $self->{plater}->object_menu;
|
||||
$self->on_plater_selection_changed(0);
|
||||
}
|
||||
|
||||
# Window menu
|
||||
my $windowMenu = Wx::Menu->new;
|
||||
{
|
||||
my $tab_offset = 0;
|
||||
if (!$self->{no_plater}) {
|
||||
$self->_append_menu_item($windowMenu, L("Select &Plater Tab\tCtrl+1"), L('Show the plater'), sub {
|
||||
$self->select_tab(0);
|
||||
}, undef, 'application_view_tile.png');
|
||||
$tab_offset += 1;
|
||||
}
|
||||
if (!$self->{no_controller}) {
|
||||
$self->_append_menu_item($windowMenu, L("Select &Controller Tab\tCtrl+T"), L('Show the printer controller'), sub {
|
||||
$self->select_tab(1);
|
||||
}, undef, 'printer_empty.png');
|
||||
$tab_offset += 1;
|
||||
}
|
||||
if ($tab_offset > 0) {
|
||||
$windowMenu->AppendSeparator();
|
||||
}
|
||||
$self->_append_menu_item($windowMenu, L("Select P&rint Settings Tab\tCtrl+2"), L('Show the print settings'), sub {
|
||||
$self->select_tab($tab_offset+0);
|
||||
}, undef, 'cog.png');
|
||||
$self->_append_menu_item($windowMenu, L("Select &Filament Settings Tab\tCtrl+3"), L('Show the filament settings'), sub {
|
||||
$self->select_tab($tab_offset+1);
|
||||
}, undef, 'spool.png');
|
||||
$self->_append_menu_item($windowMenu, L("Select Print&er Settings Tab\tCtrl+4"), L('Show the printer settings'), sub {
|
||||
$self->select_tab($tab_offset+2);
|
||||
}, undef, 'printer_empty.png');
|
||||
}
|
||||
|
||||
# View menu
|
||||
if (!$self->{no_plater}) {
|
||||
$self->{viewMenu} = Wx::Menu->new;
|
||||
# \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators,
|
||||
# as the simple numeric accelerators spoil all numeric data entry.
|
||||
# The camera control accelerators are captured by 3DScene Perl module instead.
|
||||
my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Iso'), '0'), L('Iso View') , sub { $self->select_view('iso' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Top'), '1'), L('Top View') , sub { $self->select_view('top' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Bottom'), '2'), L('Bottom View') , sub { $self->select_view('bottom' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Front'), '3'), L('Front View') , sub { $self->select_view('front' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Rear'), '4'), L('Rear View') , sub { $self->select_view('rear' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Left'), '5'), L('Left View') , sub { $self->select_view('left' ); });
|
||||
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Right'), '6'), L('Right View') , sub { $self->select_view('right' ); });
|
||||
}
|
||||
|
||||
# Help menu
|
||||
my $helpMenu = Wx::Menu->new;
|
||||
{
|
||||
$self->_append_menu_item($helpMenu, L("Prusa 3D Drivers"), L('Open the Prusa3D drivers download page in your browser'), sub {
|
||||
Wx::LaunchDefaultBrowser('http://www.prusa3d.com/drivers/');
|
||||
});
|
||||
$self->_append_menu_item($helpMenu, L("Prusa Edition Releases"), L('Open the Prusa Edition releases page in your browser'), sub {
|
||||
Wx::LaunchDefaultBrowser('http://github.com/prusa3d/slic3r/releases');
|
||||
});
|
||||
# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", 'Check for new Slic3r versions', sub {
|
||||
# wxTheApp->check_version(1);
|
||||
# });
|
||||
# $versioncheck->Enable(wxTheApp->have_version_check);
|
||||
$self->_append_menu_item($helpMenu, L("Slic3r &Website"), L('Open the Slic3r website in your browser'), sub {
|
||||
Wx::LaunchDefaultBrowser('http://slic3r.org/');
|
||||
});
|
||||
$self->_append_menu_item($helpMenu, L("Slic3r &Manual"), L('Open the Slic3r manual in your browser'), sub {
|
||||
Wx::LaunchDefaultBrowser('http://manual.slic3r.org/');
|
||||
});
|
||||
$helpMenu->AppendSeparator();
|
||||
$self->_append_menu_item($helpMenu, L("System Info"), L('Show system information'), sub {
|
||||
wxTheApp->system_info;
|
||||
});
|
||||
$self->_append_menu_item($helpMenu, L("Show &Configuration Folder"), L('Show user configuration folder (datadir)'), sub {
|
||||
Slic3r::GUI::desktop_open_datadir_folder();
|
||||
});
|
||||
$self->_append_menu_item($helpMenu, L("Report an Issue"), L('Report an issue on the Slic3r Prusa Edition'), sub {
|
||||
Wx::LaunchDefaultBrowser('http://github.com/prusa3d/slic3r/issues/new');
|
||||
});
|
||||
$self->_append_menu_item($helpMenu, L("&About Slic3r"), L('Show about dialog'), sub {
|
||||
Slic3r::GUI::about;
|
||||
});
|
||||
}
|
||||
|
||||
# menubar
|
||||
# assign menubar to frame after appending items, otherwise special items
|
||||
# will not be handled correctly
|
||||
{
|
||||
my $menubar = Wx::MenuBar->new;
|
||||
$menubar->Append($fileMenu, L("&File"));
|
||||
$menubar->Append($self->{plater_menu}, L("&Plater")) if $self->{plater_menu};
|
||||
$menubar->Append($self->{object_menu}, L("&Object")) if $self->{object_menu};
|
||||
$menubar->Append($windowMenu, L("&Window"));
|
||||
$menubar->Append($self->{viewMenu}, L("&View")) if $self->{viewMenu};
|
||||
# Add additional menus from C++
|
||||
Slic3r::GUI::add_menus($menubar, $self->{preferences_event}, $self->{lang_ch_event});
|
||||
$menubar->Append($helpMenu, L("&Help"));
|
||||
$self->SetMenuBar($menubar);
|
||||
}
|
||||
}
|
||||
|
||||
sub is_loaded {
|
||||
my ($self) = @_;
|
||||
return $self->{loaded};
|
||||
}
|
||||
|
||||
# Selection of a 3D object changed on the platter.
|
||||
sub on_plater_selection_changed {
|
||||
my ($self, $have_selection) = @_;
|
||||
return if !defined $self->{object_menu};
|
||||
$self->{object_menu}->Enable($_->GetId, $have_selection)
|
||||
for $self->{object_menu}->GetMenuItems;
|
||||
}
|
||||
|
||||
sub slice_to_png {
|
||||
my $self = shift;
|
||||
$self->{plater}->stop_background_process;
|
||||
$self->{plater}->async_apply_config;
|
||||
$appController->print_ctl()->slice_to_png();
|
||||
}
|
||||
|
||||
sub reslice_now {
|
||||
my ($self) = @_;
|
||||
$self->{plater}->reslice if $self->{plater};
|
||||
}
|
||||
|
||||
sub repair_stl {
|
||||
my $self = shift;
|
||||
|
||||
my $input_file;
|
||||
{
|
||||
my $dialog = Wx::FileDialog->new($self, L('Select the STL file to repair:'),
|
||||
wxTheApp->{app_config}->get_last_dir, "",
|
||||
&Slic3r::GUI::FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if ($dialog->ShowModal != wxID_OK) {
|
||||
$dialog->Destroy;
|
||||
return;
|
||||
}
|
||||
$input_file = $dialog->GetPaths;
|
||||
$dialog->Destroy;
|
||||
}
|
||||
|
||||
my $output_file = $input_file;
|
||||
{
|
||||
$output_file =~ s/\.[sS][tT][lL]$/_fixed.obj/;
|
||||
my $dlg = Wx::FileDialog->new($self, L("Save OBJ file (less prone to coordinate errors than STL) as:"), dirname($output_file),
|
||||
basename($output_file), &Slic3r::GUI::FILE_WILDCARDS->{obj}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
if ($dlg->ShowModal != wxID_OK) {
|
||||
$dlg->Destroy;
|
||||
return undef;
|
||||
}
|
||||
$output_file = $dlg->GetPath;
|
||||
$dlg->Destroy;
|
||||
}
|
||||
|
||||
my $tmesh = Slic3r::TriangleMesh->new;
|
||||
$tmesh->ReadSTLFile($input_file);
|
||||
$tmesh->repair;
|
||||
$tmesh->WriteOBJFile($output_file);
|
||||
Slic3r::GUI::show_info($self, L("Your file was repaired."), L("Repair"));
|
||||
}
|
||||
|
||||
sub export_config {
|
||||
my $self = shift;
|
||||
# Generate a cummulative configuration for the selected print, filaments and printer.
|
||||
my $config = wxTheApp->{preset_bundle}->full_config();
|
||||
# Validate the cummulative configuration.
|
||||
eval { $config->validate; };
|
||||
Slic3r::GUI::catch_error($self) and return;
|
||||
# Ask user for the file name for the config file.
|
||||
my $dlg = Wx::FileDialog->new($self, L('Save configuration as:'),
|
||||
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
||||
$last_config ? basename($last_config) : "config.ini",
|
||||
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef;
|
||||
$dlg->Destroy;
|
||||
if (defined $file) {
|
||||
wxTheApp->{app_config}->update_config_dir(dirname($file));
|
||||
$last_config = $file;
|
||||
$config->save($file);
|
||||
}
|
||||
}
|
||||
|
||||
# Load a config file containing a Print, Filament & Printer preset.
|
||||
sub load_config_file {
|
||||
my ($self, $file) = @_;
|
||||
if (!$file) {
|
||||
return unless Slic3r::GUI::check_unsaved_changes;
|
||||
my $dlg = Wx::FileDialog->new($self, L('Select configuration to load:'),
|
||||
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
||||
"config.ini",
|
||||
'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
return unless $dlg->ShowModal == wxID_OK;
|
||||
$file = $dlg->GetPaths;
|
||||
$dlg->Destroy;
|
||||
}
|
||||
eval { wxTheApp->{preset_bundle}->load_config_file($file); };
|
||||
# Dont proceed further if the config file cannot be loaded.
|
||||
return if Slic3r::GUI::catch_error($self);
|
||||
$_->load_current_preset for (values %{$self->{options_tabs}});
|
||||
wxTheApp->{app_config}->update_config_dir(dirname($file));
|
||||
$last_config = $file;
|
||||
}
|
||||
|
||||
sub export_configbundle {
|
||||
my ($self) = @_;
|
||||
return unless Slic3r::GUI::check_unsaved_changes;
|
||||
# validate current configuration in case it's dirty
|
||||
eval { wxTheApp->{preset_bundle}->full_config->validate; };
|
||||
Slic3r::GUI::catch_error($self) and return;
|
||||
# Ask user for a file name.
|
||||
my $dlg = Wx::FileDialog->new($self, L('Save presets bundle as:'),
|
||||
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
||||
"Slic3r_config_bundle.ini",
|
||||
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
my $file = ($dlg->ShowModal == wxID_OK) ? $dlg->GetPath : undef;
|
||||
$dlg->Destroy;
|
||||
if (defined $file) {
|
||||
# Export the config bundle.
|
||||
wxTheApp->{app_config}->update_config_dir(dirname($file));
|
||||
eval { wxTheApp->{preset_bundle}->export_configbundle($file); };
|
||||
Slic3r::GUI::catch_error($self) and return;
|
||||
}
|
||||
}
|
||||
|
||||
# Loading a config bundle with an external file name used to be used
|
||||
# to auto-install a config bundle on a fresh user account,
|
||||
# but that behavior was not documented and likely buggy.
|
||||
sub load_configbundle {
|
||||
my ($self, $file, $reset_user_profile) = @_;
|
||||
return unless Slic3r::GUI::check_unsaved_changes;
|
||||
if (!$file) {
|
||||
my $dlg = Wx::FileDialog->new($self, L('Select configuration to load:'),
|
||||
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
||||
"config.ini",
|
||||
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
return unless $dlg->ShowModal == wxID_OK;
|
||||
$file = $dlg->GetPaths;
|
||||
$dlg->Destroy;
|
||||
}
|
||||
|
||||
wxTheApp->{app_config}->update_config_dir(dirname($file));
|
||||
|
||||
my $presets_imported = 0;
|
||||
eval { $presets_imported = wxTheApp->{preset_bundle}->load_configbundle($file); };
|
||||
Slic3r::GUI::catch_error($self) and return;
|
||||
|
||||
# Load the currently selected preset into the GUI, update the preset selection box.
|
||||
foreach my $tab (values %{$self->{options_tabs}}) {
|
||||
$tab->load_current_preset;
|
||||
}
|
||||
|
||||
my $message = sprintf L("%d presets successfully imported."), $presets_imported;
|
||||
Slic3r::GUI::show_info($self, $message);
|
||||
}
|
||||
|
||||
# Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset.
|
||||
# Also update the platter with the new presets.
|
||||
sub load_config {
|
||||
my ($self, $config) = @_;
|
||||
$_->load_config($config) foreach values %{$self->{options_tabs}};
|
||||
$self->{plater}->on_config_change($config) if $self->{plater};
|
||||
}
|
||||
|
||||
sub select_tab {
|
||||
my ($self, $tab) = @_;
|
||||
$self->{tabpanel}->SetSelection($tab);
|
||||
}
|
||||
|
||||
# Set a camera direction, zoom to all objects.
|
||||
sub select_view {
|
||||
my ($self, $direction) = @_;
|
||||
if (! $self->{no_plater}) {
|
||||
$self->{plater}->select_view($direction);
|
||||
}
|
||||
}
|
||||
|
||||
sub _append_menu_item {
|
||||
my ($self, $menu, $string, $description, $cb, $id, $icon) = @_;
|
||||
$id //= &Wx::NewId();
|
||||
my $item = $menu->Append($id, $string, $description);
|
||||
$self->_set_menu_item_icon($item, $icon);
|
||||
EVT_MENU($self, $id, $cb);
|
||||
return $item;
|
||||
}
|
||||
|
||||
sub _set_menu_item_icon {
|
||||
my ($self, $menuItem, $icon) = @_;
|
||||
# SetBitmap was not available on OS X before Wx 0.9927
|
||||
if ($icon && $menuItem->can('SetBitmap')) {
|
||||
$menuItem->SetBitmap(Wx::Bitmap->new(Slic3r::var($icon), wxBITMAP_TYPE_PNG));
|
||||
}
|
||||
}
|
||||
|
||||
# Called after the Preferences dialog is closed and the program settings are saved.
|
||||
# Update the UI based on the current preferences.
|
||||
sub update_ui_from_settings {
|
||||
my ($self) = @_;
|
||||
$self->{menu_item_reslice_now}->Enable(! wxTheApp->{app_config}->get("background_processing"));
|
||||
$self->{plater}->update_ui_from_settings if ($self->{plater});
|
||||
for my $tab_name (qw(print filament printer)) {
|
||||
$self->{options_tabs}{$tab_name}->update_ui_from_settings;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,26 +0,0 @@
|
|||
package Slic3r::GUI::Plater::3D;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use List::Util qw();
|
||||
use Wx qw(:misc :pen :brush :sizer :font :cursor :keycode wxTAB_TRAVERSAL);
|
||||
use base qw(Slic3r::GUI::3DScene Class::Accessor);
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $objects, $model, $print, $config) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent);
|
||||
Slic3r::GUI::_3DScene::enable_picking($self, 1);
|
||||
Slic3r::GUI::_3DScene::enable_moving($self, 1);
|
||||
Slic3r::GUI::_3DScene::set_select_by($self, 'object');
|
||||
Slic3r::GUI::_3DScene::set_drag_by($self, 'instance');
|
||||
Slic3r::GUI::_3DScene::set_model($self, $model);
|
||||
Slic3r::GUI::_3DScene::set_print($self, $print);
|
||||
Slic3r::GUI::_3DScene::set_config($self, $config);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -1,555 +0,0 @@
|
|||
package Slic3r::GUI::Plater::3DPreview;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Slic3r::Print::State ':steps';
|
||||
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 EVT_SIZE);
|
||||
use base qw(Wx::Panel Class::Accessor);
|
||||
|
||||
use Wx::Locale gettext => 'L';
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(print gcode_preview_data enabled _loaded canvas slider_low slider_high single_layer double_slider_sizer));
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $print, $gcode_preview_data, $config) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition);
|
||||
$self->{config} = $config;
|
||||
$self->{number_extruders} = 1;
|
||||
# Show by feature type by default.
|
||||
$self->{preferred_color_mode} = 'feature';
|
||||
|
||||
# init GUI elements
|
||||
my $canvas = Slic3r::GUI::3DScene->new($self);
|
||||
Slic3r::GUI::_3DScene::enable_shader($canvas, 1);
|
||||
Slic3r::GUI::_3DScene::set_config($canvas, $config);
|
||||
$self->canvas($canvas);
|
||||
# my $slider_low = Wx::Slider->new(
|
||||
# $self, -1,
|
||||
# 0, # default
|
||||
# 0, # min
|
||||
# we set max to a bogus non-zero value because the MSW implementation of wxSlider
|
||||
# will skip drawing the slider if max <= min:
|
||||
# 1, # max
|
||||
# wxDefaultPosition,
|
||||
# wxDefaultSize,
|
||||
# wxVERTICAL | wxSL_INVERSE,
|
||||
# );
|
||||
# $self->slider_low($slider_low);
|
||||
# my $slider_high = Wx::Slider->new(
|
||||
# $self, -1,
|
||||
# 0, # default
|
||||
# 0, # min
|
||||
# we set max to a bogus non-zero value because the MSW implementation of wxSlider
|
||||
# will skip drawing the slider if max <= min:
|
||||
# 1, # max
|
||||
# wxDefaultPosition,
|
||||
# wxDefaultSize,
|
||||
# wxVERTICAL | wxSL_INVERSE,
|
||||
# );
|
||||
# $self->slider_high($slider_high);
|
||||
|
||||
# my $z_label_low = $self->{z_label_low} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
# $z_label_low->SetFont($Slic3r::GUI::small_font);
|
||||
# my $z_label_high = $self->{z_label_high} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
# $z_label_high->SetFont($Slic3r::GUI::small_font);
|
||||
|
||||
# my $z_label_low_idx = $self->{z_label_low_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
# $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,
|
||||
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
# $z_label_high_idx->SetFont($Slic3r::GUI::small_font);
|
||||
|
||||
# $self->single_layer(0);
|
||||
# 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 $choice_view_type = $self->{choice_view_type} = Wx::Choice->new($self, -1);
|
||||
$choice_view_type->Append(L("Feature type"));
|
||||
$choice_view_type->Append(L("Height"));
|
||||
$choice_view_type->Append(L("Width"));
|
||||
$choice_view_type->Append(L("Speed"));
|
||||
$choice_view_type->Append(L("Volumetric flow rate"));
|
||||
$choice_view_type->Append(L("Tool"));
|
||||
$choice_view_type->SetSelection(0);
|
||||
|
||||
# the following value needs to be changed if new items are added into $choice_view_type before "Tool"
|
||||
$self->{tool_idx} = 5;
|
||||
|
||||
my $label_show_features = $self->{label_show_features} = Wx::StaticText->new($self, -1, L("Show"));
|
||||
|
||||
my $combochecklist_features = $self->{combochecklist_features} = Wx::ComboCtrl->new();
|
||||
$combochecklist_features->Create($self, -1, L("Feature types"), wxDefaultPosition, [200, -1], wxCB_READONLY);
|
||||
my $feature_text = L("Feature types");
|
||||
my $feature_items = L("Perimeter")."|"
|
||||
.L("External perimeter")."|"
|
||||
.L("Overhang perimeter")."|"
|
||||
.L("Internal infill")."|"
|
||||
.L("Solid infill")."|"
|
||||
.L("Top solid infill")."|"
|
||||
.L("Bridge infill")."|"
|
||||
.L("Gap fill")."|"
|
||||
.L("Skirt")."|"
|
||||
.L("Support material")."|"
|
||||
.L("Support material interface")."|"
|
||||
.L("Wipe tower")."|"
|
||||
.L("Custom");
|
||||
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_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_shells = $self->{checkbox_shells} = Wx::CheckBox->new($self, -1, L("Shells"));
|
||||
|
||||
# my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
# my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
# my $vsizer_outer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
# $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, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
# $hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
||||
# $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
# $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, 0, 0, 0);
|
||||
# $hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
||||
# $vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
# $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);
|
||||
$bottom_sizer->Add($label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5);
|
||||
$bottom_sizer->Add($choice_view_type, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
$bottom_sizer->AddSpacer(10);
|
||||
$bottom_sizer->Add($label_show_features, 0, wxALIGN_CENTER_VERTICAL, 5);
|
||||
$bottom_sizer->Add($combochecklist_features, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
$bottom_sizer->AddSpacer(20);
|
||||
$bottom_sizer->Add($checkbox_travel, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
$bottom_sizer->AddSpacer(10);
|
||||
$bottom_sizer->Add($checkbox_retractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
$bottom_sizer->AddSpacer(10);
|
||||
$bottom_sizer->Add($checkbox_unretractions, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
$bottom_sizer->AddSpacer(10);
|
||||
$bottom_sizer->Add($checkbox_shells, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5);
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$sizer->Add($canvas, 1, wxALL | wxEXPAND, 0);
|
||||
# $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);
|
||||
$main_sizer->Add($sizer, 1, wxALL | wxEXPAND, 0);
|
||||
$main_sizer->Add($bottom_sizer, 0, wxALL | wxEXPAND, 0);
|
||||
|
||||
# EVT_SLIDER($self, $slider_low, sub {
|
||||
# $slider_high->SetValue($slider_low->GetValue) if $self->single_layer;
|
||||
# $self->set_z_idx_low ($slider_low ->GetValue)
|
||||
# });
|
||||
# EVT_SLIDER($self, $slider_high, sub {
|
||||
# $slider_low->SetValue($slider_high->GetValue) if $self->single_layer;
|
||||
# $self->set_z_idx_high($slider_high->GetValue)
|
||||
# });
|
||||
# EVT_KEY_DOWN($canvas, sub {
|
||||
# my ($s, $event) = @_;
|
||||
# Slic3r::GUI::update_double_slider_from_canvas($event);
|
||||
# my $key = $event->GetKeyCode;
|
||||
# if ($event->HasModifiers) {
|
||||
# $event->Skip;
|
||||
# } else {
|
||||
# if ($key == ord('U')) {
|
||||
# $slider_high->SetValue($slider_high->GetValue + 1);
|
||||
# $slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown());
|
||||
# $self->set_z_idx_high($slider_high->GetValue);
|
||||
# } elsif ($key == ord('D')) {
|
||||
# $slider_high->SetValue($slider_high->GetValue - 1);
|
||||
# $slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown());
|
||||
# $self->set_z_idx_high($slider_high->GetValue);
|
||||
# } elsif ($key == ord('S')) {
|
||||
# $checkbox_singlelayer->SetValue(! $checkbox_singlelayer->GetValue());
|
||||
# $self->single_layer($checkbox_singlelayer->GetValue());
|
||||
# if ($self->single_layer) {
|
||||
# $slider_low->SetValue($slider_high->GetValue);
|
||||
# $self->set_z_idx_high($slider_high->GetValue);
|
||||
# }
|
||||
# } else {
|
||||
# $event->Skip;
|
||||
# }
|
||||
# }
|
||||
# });
|
||||
# EVT_KEY_DOWN($slider_low, sub {
|
||||
# my ($s, $event) = @_;
|
||||
# my $key = $event->GetKeyCode;
|
||||
# if ($event->HasModifiers) {
|
||||
# $event->Skip;
|
||||
# } else {
|
||||
# if ($key == WXK_LEFT) {
|
||||
# } elsif ($key == WXK_RIGHT) {
|
||||
# $slider_high->SetFocus;
|
||||
# } else {
|
||||
# $event->Skip;
|
||||
# }
|
||||
# }
|
||||
# });
|
||||
# EVT_KEY_DOWN($slider_high, sub {
|
||||
# my ($s, $event) = @_;
|
||||
# my $key = $event->GetKeyCode;
|
||||
# if ($event->HasModifiers) {
|
||||
# $event->Skip;
|
||||
# } else {
|
||||
# if ($key == WXK_LEFT) {
|
||||
# $slider_low->SetFocus;
|
||||
# } elsif ($key == WXK_RIGHT) {
|
||||
# } else {
|
||||
# $event->Skip;
|
||||
# }
|
||||
# }
|
||||
# });
|
||||
# EVT_CHECKBOX($self, $checkbox_singlelayer, sub {
|
||||
# $self->single_layer($checkbox_singlelayer->GetValue());
|
||||
# if ($self->single_layer) {
|
||||
# $slider_low->SetValue($slider_high->GetValue);
|
||||
# $self->set_z_idx_high($slider_high->GetValue);
|
||||
# }
|
||||
# });
|
||||
EVT_CHOICE($self, $choice_view_type, sub {
|
||||
my $selection = $choice_view_type->GetCurrentSelection();
|
||||
$self->{preferred_color_mode} = ($selection == $self->{tool_idx}) ? 'tool' : 'feature';
|
||||
$self->gcode_preview_data->set_type($selection);
|
||||
$self->reload_print;
|
||||
});
|
||||
EVT_CHECKLISTBOX($self, $combochecklist_features, sub {
|
||||
my $flags = Slic3r::GUI::combochecklist_get_flags($combochecklist_features);
|
||||
|
||||
$self->gcode_preview_data->set_extrusion_flags($flags);
|
||||
$self->refresh_print;
|
||||
});
|
||||
EVT_CHECKBOX($self, $checkbox_travel, sub {
|
||||
$self->gcode_preview_data->set_travel_visible($checkbox_travel->IsChecked());
|
||||
$self->refresh_print;
|
||||
});
|
||||
EVT_CHECKBOX($self, $checkbox_retractions, sub {
|
||||
$self->gcode_preview_data->set_retractions_visible($checkbox_retractions->IsChecked());
|
||||
$self->refresh_print;
|
||||
});
|
||||
EVT_CHECKBOX($self, $checkbox_unretractions, sub {
|
||||
$self->gcode_preview_data->set_unretractions_visible($checkbox_unretractions->IsChecked());
|
||||
$self->refresh_print;
|
||||
});
|
||||
EVT_CHECKBOX($self, $checkbox_shells, sub {
|
||||
$self->gcode_preview_data->set_shells_visible($checkbox_shells->IsChecked());
|
||||
$self->refresh_print;
|
||||
});
|
||||
|
||||
EVT_SIZE($self, sub {
|
||||
my ($s, $event) = @_;
|
||||
$event->Skip;
|
||||
$self->Refresh;
|
||||
});
|
||||
|
||||
$self->SetSizer($main_sizer);
|
||||
$self->SetMinSize($self->GetSize);
|
||||
$sizer->SetSizeHints($self);
|
||||
|
||||
# init canvas
|
||||
$self->print($print);
|
||||
$self->gcode_preview_data($gcode_preview_data);
|
||||
|
||||
# sets colors for gcode preview extrusion roles
|
||||
my @extrusion_roles_colors = (
|
||||
'Perimeter' => 'FFFF66',
|
||||
'External perimeter' => 'FFA500',
|
||||
'Overhang perimeter' => '0000FF',
|
||||
'Internal infill' => 'B1302A',
|
||||
'Solid infill' => 'D732D7',
|
||||
'Top solid infill' => 'FF1A1A',
|
||||
'Bridge infill' => '9999FF',
|
||||
'Gap fill' => 'FFFFFF',
|
||||
'Skirt' => '845321',
|
||||
'Support material' => '00FF00',
|
||||
'Support material interface' => '008000',
|
||||
'Wipe tower' => 'B3E3AB',
|
||||
'Custom' => '28CC94',
|
||||
);
|
||||
$self->gcode_preview_data->set_extrusion_paths_colors(\@extrusion_roles_colors);
|
||||
|
||||
$self->show_hide_ui_elements('none');
|
||||
$self->reload_print;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub reload_print {
|
||||
my ($self, $force) = @_;
|
||||
|
||||
Slic3r::GUI::_3DScene::reset_volumes($self->canvas);
|
||||
$self->_loaded(0);
|
||||
|
||||
if (! $self->IsShown && ! $force) {
|
||||
# $self->{reload_delayed} = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
$self->load_print;
|
||||
}
|
||||
|
||||
sub refresh_print {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_loaded(0);
|
||||
|
||||
if (! $self->IsShown) {
|
||||
return;
|
||||
}
|
||||
|
||||
$self->load_print;
|
||||
}
|
||||
|
||||
sub reset_gcode_preview_data {
|
||||
my ($self) = @_;
|
||||
$self->gcode_preview_data->reset;
|
||||
Slic3r::GUI::_3DScene::reset_legend_texture();
|
||||
}
|
||||
|
||||
sub load_print {
|
||||
my ($self) = @_;
|
||||
|
||||
return if $self->_loaded;
|
||||
|
||||
# we require that there's at least one object and the posSlice step
|
||||
# is performed on all of them (this ensures that _shifted_copies was
|
||||
# populated and we know the number of layers)
|
||||
my $n_layers = 0;
|
||||
if ($self->print->object_step_done(STEP_SLICE)) {
|
||||
my %z = (); # z => 1
|
||||
foreach my $object (@{$self->{print}->objects}) {
|
||||
foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
|
||||
$z{$layer->print_z} = 1;
|
||||
}
|
||||
}
|
||||
$self->{layers_z} = [ sort { $a <=> $b } keys %z ];
|
||||
$n_layers = scalar(@{$self->{layers_z}});
|
||||
}
|
||||
|
||||
if ($n_layers == 0) {
|
||||
$self->reset_sliders;
|
||||
Slic3r::GUI::_3DScene::reset_legend_texture();
|
||||
$self->canvas->Refresh; # clears canvas
|
||||
return;
|
||||
}
|
||||
|
||||
if ($self->{preferred_color_mode} eq 'tool_or_feature') {
|
||||
# It is left to Slic3r to decide whether the print shall be colored by the tool or by the feature.
|
||||
# Color by feature if it is a single extruder print.
|
||||
my $extruders = $self->{print}->extruders;
|
||||
my $type = (scalar(@{$extruders}) > 1) ? $self->{tool_idx} : 0;
|
||||
$self->gcode_preview_data->set_type($type);
|
||||
$self->{choice_view_type}->SetSelection($type);
|
||||
# If the ->SetSelection changed the following line, revert it to "decide yourself".
|
||||
$self->{preferred_color_mode} = 'tool_or_feature';
|
||||
}
|
||||
|
||||
# Collect colors per extruder.
|
||||
my @colors = ();
|
||||
if (! $self->gcode_preview_data->empty() || $self->gcode_preview_data->type == $self->{tool_idx}) {
|
||||
my @extruder_colors = @{$self->{config}->extruder_colour};
|
||||
my @filament_colors = @{$self->{config}->filament_colour};
|
||||
for (my $i = 0; $i <= $#extruder_colors; $i += 1) {
|
||||
my $color = $extruder_colors[$i];
|
||||
$color = $filament_colors[$i] if (! defined($color) || $color !~ m/^#[[:xdigit:]]{6}/);
|
||||
$color = '#FFFFFF' if (! defined($color) || $color !~ m/^#[[:xdigit:]]{6}/);
|
||||
push @colors, $color;
|
||||
}
|
||||
}
|
||||
|
||||
if ($self->IsShown) {
|
||||
# used to set the sliders to the extremes of the current zs range
|
||||
$self->{force_sliders_full_range} = 0;
|
||||
|
||||
if ($self->gcode_preview_data->empty) {
|
||||
# load skirt and brim
|
||||
Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print);
|
||||
Slic3r::GUI::_3DScene::load_preview($self->canvas, \@colors);
|
||||
$self->show_hide_ui_elements('simple');
|
||||
} else {
|
||||
$self->{force_sliders_full_range} = (Slic3r::GUI::_3DScene::get_volumes_count($self->canvas) == 0);
|
||||
Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print);
|
||||
Slic3r::GUI::_3DScene::load_gcode_preview($self->canvas, $self->gcode_preview_data, \@colors);
|
||||
$self->show_hide_ui_elements('full');
|
||||
|
||||
# recalculates zs and update sliders accordingly
|
||||
$self->{layers_z} = Slic3r::GUI::_3DScene::get_current_print_zs($self->canvas, 1);
|
||||
$n_layers = scalar(@{$self->{layers_z}});
|
||||
if ($n_layers == 0) {
|
||||
# all layers filtered out
|
||||
$self->reset_sliders;
|
||||
$self->canvas->Refresh; # clears canvas
|
||||
}
|
||||
}
|
||||
|
||||
$self->update_sliders($n_layers) if ($n_layers > 0);
|
||||
$self->_loaded(1);
|
||||
}
|
||||
}
|
||||
|
||||
sub reset_sliders {
|
||||
my ($self) = @_;
|
||||
$self->enabled(0);
|
||||
# $self->set_z_range(0,0);
|
||||
# $self->slider_low->Hide;
|
||||
# $self->slider_high->Hide;
|
||||
# $self->{z_label_low}->SetLabel("");
|
||||
# $self->{z_label_high}->SetLabel("");
|
||||
# $self->{z_label_low_idx}->SetLabel("");
|
||||
# $self->{z_label_high_idx}->SetLabel("");
|
||||
|
||||
Slic3r::GUI::reset_double_slider();
|
||||
$self->double_slider_sizer->Hide(0);
|
||||
}
|
||||
|
||||
sub update_sliders
|
||||
{
|
||||
my ($self, $n_layers) = @_;
|
||||
|
||||
# my $z_idx_low = $self->slider_low->GetValue;
|
||||
# my $z_idx_high = $self->slider_high->GetValue;
|
||||
$self->enabled(1);
|
||||
# $self->slider_low->SetRange(0, $n_layers - 1);
|
||||
# $self->slider_high->SetRange(0, $n_layers - 1);
|
||||
|
||||
# if ($self->{force_sliders_full_range}) {
|
||||
# $z_idx_low = 0;
|
||||
# $z_idx_high = $n_layers - 1;
|
||||
# } 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)
|
||||
# if (defined($self->{z_low})) {
|
||||
# for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
|
||||
# if ($self->{layers_z}[$i] <= $self->{z_low}) {
|
||||
# $z_idx_low = $i;
|
||||
# last;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# if (defined($self->{z_high})) {
|
||||
# for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
|
||||
# if ($self->{layers_z}[$i] <= $self->{z_high}) {
|
||||
# $z_idx_high = $i;
|
||||
# last;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# } elsif ($z_idx_high >= $n_layers) {
|
||||
# # Out of range. Disable 'single layer' view.
|
||||
# $self->single_layer(0);
|
||||
# $self->{checkbox_singlelayer}->SetValue(0);
|
||||
# $z_idx_low = 0;
|
||||
# $z_idx_high = $n_layers - 1;
|
||||
# } else {
|
||||
# $z_idx_low = 0;
|
||||
# $z_idx_high = $n_layers - 1;
|
||||
# }
|
||||
|
||||
# $self->slider_low->SetValue($z_idx_low);
|
||||
# $self->slider_high->SetValue($z_idx_high);
|
||||
# $self->slider_low->Show;
|
||||
# $self->slider_high->Show;
|
||||
# $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;
|
||||
}
|
||||
|
||||
sub set_z_range
|
||||
{
|
||||
my ($self, $z_low, $z_high) = @_;
|
||||
|
||||
return if !$self->enabled;
|
||||
$self->{z_low} = $z_low;
|
||||
$self->{z_high} = $z_high;
|
||||
$self->{z_label_low}->SetLabel(sprintf '%.2f', $z_low);
|
||||
$self->{z_label_high}->SetLabel(sprintf '%.2f', $z_high);
|
||||
|
||||
my $layers_z = Slic3r::GUI::_3DScene::get_current_print_zs($self->canvas, 0);
|
||||
for (my $i = 0; $i < scalar(@{$layers_z}); $i += 1) {
|
||||
if (($z_low - 1e-6 < @{$layers_z}[$i]) && (@{$layers_z}[$i] < $z_low + 1e-6)) {
|
||||
$self->{z_label_low_idx}->SetLabel(sprintf '%d', $i + 1);
|
||||
last;
|
||||
}
|
||||
}
|
||||
for (my $i = 0; $i < scalar(@{$layers_z}); $i += 1) {
|
||||
if (($z_high - 1e-6 < @{$layers_z}[$i]) && (@{$layers_z}[$i] < $z_high + 1e-6)) {
|
||||
$self->{z_label_high_idx}->SetLabel(sprintf '%d', $i + 1);
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
Slic3r::GUI::_3DScene::set_toolpaths_range($self->canvas, $z_low - 1e-6, $z_high + 1e-6);
|
||||
$self->canvas->Refresh if $self->IsShown;
|
||||
}
|
||||
|
||||
sub set_z_idx_low
|
||||
{
|
||||
my ($self, $idx_low) = @_;
|
||||
if ($self->enabled) {
|
||||
my $idx_high = $self->slider_high->GetValue;
|
||||
if ($idx_low >= $idx_high) {
|
||||
$idx_high = $idx_low;
|
||||
$self->slider_high->SetValue($idx_high);
|
||||
}
|
||||
$self->set_z_range($self->{layers_z}[$idx_low], $self->{layers_z}[$idx_high]);
|
||||
}
|
||||
}
|
||||
|
||||
sub set_z_idx_high
|
||||
{
|
||||
my ($self, $idx_high) = @_;
|
||||
if ($self->enabled) {
|
||||
my $idx_low = $self->slider_low->GetValue;
|
||||
if ($idx_low > $idx_high) {
|
||||
$idx_low = $idx_high;
|
||||
$self->slider_low->SetValue($idx_low);
|
||||
}
|
||||
$self->set_z_range($self->{layers_z}[$idx_low], $self->{layers_z}[$idx_high]);
|
||||
}
|
||||
}
|
||||
|
||||
sub set_number_extruders {
|
||||
my ($self, $number_extruders) = @_;
|
||||
if ($self->{number_extruders} != $number_extruders) {
|
||||
$self->{number_extruders} = $number_extruders;
|
||||
my $type = ($number_extruders > 1) ?
|
||||
$self->{tool_idx} # color by a tool number
|
||||
: 0; # color by a feature type
|
||||
$self->{choice_view_type}->SetSelection($type);
|
||||
$self->gcode_preview_data->set_type($type);
|
||||
$self->{preferred_color_mode} = ($type == $self->{tool_idx}) ? 'tool_or_feature' : 'feature';
|
||||
}
|
||||
}
|
||||
|
||||
sub show_hide_ui_elements {
|
||||
my ($self, $what) = @_;
|
||||
my $method = ($what eq 'full') ? 'Enable' : 'Disable';
|
||||
$self->{$_}->$method for qw(label_show_features combochecklist_features checkbox_travel checkbox_retractions checkbox_unretractions checkbox_shells);
|
||||
$method = ($what eq 'none') ? 'Disable' : 'Enable';
|
||||
$self->{$_}->$method for qw(label_view_type choice_view_type);
|
||||
}
|
||||
|
||||
# Called by the Platter wxNotebook when this page is activated.
|
||||
sub OnActivate {
|
||||
# my ($self) = @_;
|
||||
# $self->reload_print(1) if ($self->{reload_delayed});
|
||||
}
|
||||
|
||||
1;
|
||||
Binary file not shown.
1
resources/icons/bed/mk2.svg
Normal file
1
resources/icons/bed/mk2.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 61 KiB |
1
resources/icons/bed/mk3.svg
Normal file
1
resources/icons/bed/mk3.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 61 KiB |
1
resources/icons/bed/sl1.svg
Normal file
1
resources/icons/bed/sl1.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
|
|
@ -3,7 +3,7 @@ msgstr ""
|
|||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-01-17 13:39+0100\n"
|
||||
"PO-Revision-Date: 2019-01-21 11:25+0100\n"
|
||||
"PO-Revision-Date: 2019-02-19 14:42+0100\n"
|
||||
"Last-Translator: Oleksandra Iushchenko <yusanka@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: uk\n"
|
||||
|
|
@ -624,48 +624,48 @@ msgstr ""
|
|||
|
||||
#: src/slic3r/GUI/GLGizmo.cpp:2207
|
||||
msgid "Left mouse click - add point"
|
||||
msgstr ""
|
||||
msgstr "Ліва кнопка миші - додати точку"
|
||||
|
||||
#: src/slic3r/GUI/GLGizmo.cpp:2208
|
||||
msgid "Right mouse click - remove point"
|
||||
msgstr ""
|
||||
msgstr "Права кнопка миші - видалити точку"
|
||||
|
||||
#: src/slic3r/GUI/GLGizmo.cpp:2211
|
||||
msgid "Generate points automatically"
|
||||
msgstr ""
|
||||
msgstr "Генерувати точки автоматично"
|
||||
|
||||
#: src/slic3r/GUI/GLGizmo.cpp:2212
|
||||
msgid "Remove all points"
|
||||
msgstr ""
|
||||
msgstr "Видалити всі точки"
|
||||
|
||||
#: src/slic3r/GUI/GLGizmo.cpp:2245
|
||||
msgid "SLA Support Points"
|
||||
msgstr ""
|
||||
msgstr "Точки SLA підтримки"
|
||||
|
||||
#: src/slic3r/GUI/GLGizmo.cpp:2268 src/slic3r/GUI/GLGizmo.cpp:2468
|
||||
msgid "Rotate lower part upwards"
|
||||
msgstr ""
|
||||
msgstr "Повернути нижню частину вгору"
|
||||
|
||||
#: src/slic3r/GUI/GLGizmo.cpp:2269 src/slic3r/GUI/GLGizmo.cpp:2470
|
||||
msgid "Perform cut"
|
||||
msgstr ""
|
||||
msgstr "Виконати розріз"
|
||||
|
||||
#: src/slic3r/GUI/GLGizmo.cpp:2276
|
||||
msgid "Cut object:"
|
||||
msgstr ""
|
||||
msgstr "Розрізати об'єкт:"
|
||||
|
||||
#: src/slic3r/GUI/GLGizmo.cpp:2356 src/slic3r/GUI/GLGizmo.cpp:2461
|
||||
#: src/libslic3r/PrintConfig.cpp:3016
|
||||
msgid "Cut"
|
||||
msgstr ""
|
||||
msgstr "Розрізати"
|
||||
|
||||
#: src/slic3r/GUI/GLGizmo.cpp:2466
|
||||
msgid "Keep upper part"
|
||||
msgstr ""
|
||||
msgstr "Залишити верхню частину"
|
||||
|
||||
#: src/slic3r/GUI/GLGizmo.cpp:2467
|
||||
msgid "Keep lower part"
|
||||
msgstr ""
|
||||
msgstr "Залишити нижню частину"
|
||||
|
||||
#: src/slic3r/GUI/GUI.cpp:242
|
||||
msgid "Notice"
|
||||
|
|
@ -5636,7 +5636,7 @@ msgstr ""
|
|||
|
||||
#: src/libslic3r/PrintConfig.cpp:3017
|
||||
msgid "Cut model at the given Z."
|
||||
msgstr ""
|
||||
msgstr "Розрізати модель за заданим Z."
|
||||
|
||||
#: src/libslic3r/PrintConfig.cpp:3022
|
||||
msgid "Dont arrange"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
min_slic3r_version = 1.42.0-alpha6
|
||||
0.8.0-alpha7
|
||||
0.8.0-alpha6
|
||||
min_slic3r_version = 1.42.0-alpha
|
||||
0.8.0-alpha
|
||||
0.4.0-alpha4 Updated SLA profiles
|
||||
0.4.0-alpha3 Update of SLA profiles
|
||||
0.4.0-alpha2 First SLA profiles
|
||||
min_slic3r_version = 1.41.3-alpha
|
||||
0.4.0 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
|
||||
min_slic3r_version = 1.41.1
|
||||
0.3.4 Changelog: https://github.com/prusa3d/Slic3r-settings/blob/master/live/PrusaResearch/changelog.txt
|
||||
0.3.3 Prusament PETG released
|
||||
0.3.2 New MK2.5 and MK3 FW versions
|
||||
0.3.1 New MK2.5 and MK3 FW versions
|
||||
|
|
@ -34,6 +41,7 @@ min_slic3r_version = 1.41.0-alpha
|
|||
0.2.0-alpha1 added initial profiles for the i3 MK3 Multi Material Upgrade 2.0
|
||||
0.2.0-alpha moved machine limits from the start G-code to the new print profile parameters
|
||||
min_slic3r_version = 1.40.0
|
||||
0.1.12 New MK2.5 and MK3 FW versions
|
||||
0.1.11 fw version changed to 3.3.1
|
||||
0.1.10 MK3 jerk and acceleration update
|
||||
0.1.9 edited support extrusion width for 0.25 and 0.6 nozzles
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
name = Prusa Research
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the Slic3r configuration to be downgraded.
|
||||
config_version = 0.4.0-alpha4
|
||||
config_version = 0.8.0-alpha7
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/
|
||||
|
||||
|
|
@ -15,40 +15,71 @@ config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/ma
|
|||
#for example by the melt zone size, or whether the nozzle is hardened.
|
||||
# Printer model name will be shown by the installation wizard.
|
||||
|
||||
[printer_model:MK3S]
|
||||
name = Original Prusa i3 MK3S
|
||||
variants = 0.4; 0.25; 0.6
|
||||
technology = FFF
|
||||
family = MK3
|
||||
|
||||
[printer_model:MK3]
|
||||
name = Original Prusa i3 MK3
|
||||
variants = 0.4; 0.25; 0.6
|
||||
technology = FFF
|
||||
family = MK3
|
||||
|
||||
[printer_model:MK3SMMU2S]
|
||||
name = Original Prusa i3 MK3S MMU2S
|
||||
variants = 0.4
|
||||
technology = FFF
|
||||
family = MK3
|
||||
|
||||
[printer_model:MK3MMU2]
|
||||
name = Original Prusa i3 MK3 MMU2
|
||||
variants = 0.4
|
||||
technology = FFF
|
||||
family = MK3
|
||||
|
||||
[printer_model:MK2.5S]
|
||||
name = Original Prusa i3 MK2.5S
|
||||
variants = 0.4; 0.25; 0.6
|
||||
technology = FFF
|
||||
family = MK2.5
|
||||
|
||||
[printer_model:MK2.5]
|
||||
name = Original Prusa i3 MK2.5
|
||||
variants = 0.4; 0.25; 0.6
|
||||
technology = FFF
|
||||
family = MK2.5
|
||||
|
||||
[printer_model:MK2S]
|
||||
name = Original Prusa i3 MK2/S
|
||||
variants = 0.4; 0.25; 0.6
|
||||
technology = FFF
|
||||
|
||||
[printer_model:MK3MMU2]
|
||||
name = Original Prusa i3 MK3 MMU 2.0
|
||||
[printer_model:MK2.5SMMU2S]
|
||||
name = Original Prusa i3 MK2.5S MMU2S
|
||||
variants = 0.4
|
||||
technology = FFF
|
||||
|
||||
[printer_model:MK2SMM]
|
||||
name = Original Prusa i3 MK2/S MMU 1.0
|
||||
variants = 0.4; 0.6
|
||||
technology = FFF
|
||||
family = MK2.5
|
||||
|
||||
[printer_model:MK2.5MMU2]
|
||||
name = Original Prusa i3 MK2.5 MMU 2.0
|
||||
name = Original Prusa i3 MK2.5 MMU2
|
||||
variants = 0.4
|
||||
technology = FFF
|
||||
family = MK2.5
|
||||
|
||||
[printer_model:MK2S]
|
||||
name = Original Prusa i3 MK2S
|
||||
variants = 0.4; 0.25; 0.6
|
||||
technology = FFF
|
||||
family = MK2
|
||||
|
||||
[printer_model:MK2SMM]
|
||||
name = Original Prusa i3 MK2S MMU1
|
||||
variants = 0.4; 0.6
|
||||
technology = FFF
|
||||
family = MK2
|
||||
|
||||
[printer_model:SL1]
|
||||
name = Original Prusa SL1
|
||||
variants = default
|
||||
technology = SLA
|
||||
family = SL1
|
||||
|
||||
# All presets starting with asterisk, for example *common*, are intermediate and they will
|
||||
# not make it into the user interface.
|
||||
|
|
@ -197,6 +228,7 @@ support_material_interface_spacing = 0.1
|
|||
support_material_synchronize_layers = 1
|
||||
support_material_threshold = 80
|
||||
support_material_with_sheath = 1
|
||||
wipe_tower_bridging = 8
|
||||
|
||||
# XXXXXXXXXXXXXXXXXXXX
|
||||
# XXX--- 0.05mm ---XXX
|
||||
|
|
@ -232,11 +264,15 @@ inherits = *0.05mm*
|
|||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1
|
||||
infill_extrusion_width = 0.5
|
||||
|
||||
# MK3 #
|
||||
[print:0.05mm ULTRADETAIL MK3]
|
||||
inherits = *0.05mm*; *MK3*
|
||||
fill_pattern = gyroid
|
||||
fill_density = 15%
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
|
||||
top_infill_extrusion_width = 0.4
|
||||
|
||||
# MK2 #
|
||||
[print:0.05mm ULTRADETAIL 0.25 nozzle]
|
||||
inherits = *0.05mm*; *0.25nozzle*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.25 and num_extruders==1
|
||||
|
|
@ -248,14 +284,53 @@ small_perimeter_speed = 15
|
|||
solid_infill_speed = 20
|
||||
support_material_speed = 20
|
||||
|
||||
# MK3 #
|
||||
[print:0.05mm ULTRADETAIL 0.25 nozzle MK3]
|
||||
inherits = *0.05mm*; *0.25nozzle*; *MK3*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.25 and num_extruders==1
|
||||
|
||||
# XXXXXXXXXXXXXXXXXXXX
|
||||
# XXX--- 0.07mm ---XXX
|
||||
# XXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
[print:*0.07mm*]
|
||||
inherits = *common*
|
||||
bottom_solid_layers = 8
|
||||
bridge_acceleration = 300
|
||||
bridge_flow_ratio = 0.7
|
||||
bridge_speed = 20
|
||||
default_acceleration = 500
|
||||
external_perimeter_speed = 20
|
||||
fill_density = 15%
|
||||
first_layer_acceleration = 500
|
||||
gap_fill_speed = 20
|
||||
infill_acceleration = 800
|
||||
infill_speed = 40
|
||||
max_print_speed = 80
|
||||
small_perimeter_speed = 20
|
||||
solid_infill_speed = 40
|
||||
support_material_extrusion_width = 0.3
|
||||
support_material_spacing = 1.5
|
||||
layer_height = 0.07
|
||||
perimeter_acceleration = 300
|
||||
perimeter_speed = 30
|
||||
perimeters = 3
|
||||
support_material_speed = 40
|
||||
top_solid_infill_speed = 30
|
||||
top_solid_layers = 11
|
||||
|
||||
# MK3 #
|
||||
[print:0.07mm ULTRADETAIL MK3]
|
||||
inherits = *0.07mm*; *MK3*
|
||||
fill_pattern = gyroid
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
|
||||
top_infill_extrusion_width = 0.4
|
||||
|
||||
# XXXXXXXXXXXXXXXXXXXX
|
||||
# XXX--- 0.10mm ---XXX
|
||||
# XXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
# MK2 #
|
||||
[print:*0.10mm*]
|
||||
inherits = *common*
|
||||
bottom_solid_layers = 7
|
||||
|
|
@ -265,6 +340,7 @@ layer_height = 0.1
|
|||
perimeter_acceleration = 800
|
||||
top_solid_layers = 9
|
||||
|
||||
# MK2 #
|
||||
[print:0.10mm DETAIL]
|
||||
inherits = *0.10mm*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.4 and num_extruders==1
|
||||
|
|
@ -274,19 +350,23 @@ infill_speed = 60
|
|||
perimeter_speed = 50
|
||||
solid_infill_speed = 50
|
||||
|
||||
# MK3 #
|
||||
[print:0.10mm DETAIL MK3]
|
||||
inherits = *0.10mm*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and ! single_extruder_multi_material
|
||||
external_perimeter_speed = 35
|
||||
external_perimeter_speed = 25
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 200
|
||||
infill_speed = 80
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
solid_infill_speed = 200
|
||||
solid_infill_speed = 80
|
||||
top_infill_extrusion_width = 0.4
|
||||
top_solid_infill_speed = 50
|
||||
top_solid_infill_speed = 40
|
||||
fill_pattern = gyroid
|
||||
fill_density = 15%
|
||||
|
||||
# MK2 #
|
||||
[print:0.10mm DETAIL 0.25 nozzle]
|
||||
inherits = *0.10mm*; *0.25nozzle*
|
||||
bridge_acceleration = 600
|
||||
|
|
@ -300,6 +380,7 @@ small_perimeter_speed = 15
|
|||
solid_infill_speed = 40
|
||||
top_solid_infill_speed = 30
|
||||
|
||||
# MK3 #
|
||||
[print:0.10mm DETAIL 0.25 nozzle MK3]
|
||||
inherits = *0.10mm*; *0.25nozzle*; *MK3*
|
||||
bridge_speed = 30
|
||||
|
|
@ -312,6 +393,7 @@ perimeter_speed = 45
|
|||
solid_infill_speed = 200
|
||||
top_solid_infill_speed = 50
|
||||
|
||||
# MK3 #
|
||||
[print:0.10mm DETAIL 0.6 nozzle MK3]
|
||||
inherits = *0.10mm*; *0.6nozzle*; *MK3*
|
||||
bridge_speed = 30
|
||||
|
|
@ -341,6 +423,7 @@ solid_infill_speed = 50
|
|||
top_infill_extrusion_width = 0.4
|
||||
top_solid_layers = 7
|
||||
|
||||
# MK2 #
|
||||
[print:0.15mm 100mms Linear Advance]
|
||||
inherits = *0.15mm*
|
||||
bridge_flow_ratio = 0.95
|
||||
|
|
@ -354,11 +437,13 @@ solid_infill_speed = 100
|
|||
support_material_speed = 60
|
||||
top_solid_infill_speed = 70
|
||||
|
||||
# MK2 #
|
||||
[print:0.15mm OPTIMAL]
|
||||
inherits = *0.15mm*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4
|
||||
top_infill_extrusion_width = 0.45
|
||||
|
||||
# MK2 #
|
||||
[print:0.15mm OPTIMAL 0.25 nozzle]
|
||||
inherits = *0.15mm*; *0.25nozzle*
|
||||
bridge_acceleration = 600
|
||||
|
|
@ -373,11 +458,27 @@ small_perimeter_speed = 15
|
|||
solid_infill_speed = 40
|
||||
top_solid_infill_speed = 30
|
||||
|
||||
# MK2 #
|
||||
[print:0.15mm OPTIMAL 0.6 nozzle]
|
||||
inherits = *0.15mm*; *0.6nozzle*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6
|
||||
|
||||
[print:0.15mm OPTIMAL MK3]
|
||||
# MK3 #
|
||||
[print:0.15mm QUALITY MK3]
|
||||
inherits = *0.15mm*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
|
||||
external_perimeter_speed = 25
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 80
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
solid_infill_speed = 80
|
||||
top_solid_infill_speed = 40
|
||||
fill_pattern = gyroid
|
||||
fill_density = 15%
|
||||
|
||||
[print:0.15mm SPEED MK3]
|
||||
inherits = *0.15mm*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
|
||||
|
|
@ -385,14 +486,15 @@ external_perimeter_speed = 35
|
|||
infill_acceleration = 1250
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
perimeter_speed = 60
|
||||
solid_infill_speed = 200
|
||||
top_solid_infill_speed = 50
|
||||
|
||||
[print:0.15mm OPTIMAL MK3 SOLUBLE FULL]
|
||||
inherits = 0.15mm OPTIMAL MK3; *soluble_support*
|
||||
# MK3 MMU #
|
||||
[print:0.15mm SOLUBLE FULL MK3]
|
||||
inherits = 0.15mm SPEED MK3; *soluble_support*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1
|
||||
notes = Set your solluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder
|
||||
notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder
|
||||
support_material_extruder = 5
|
||||
support_material_interface_extruder = 5
|
||||
perimeter_speed = 40
|
||||
|
|
@ -400,31 +502,35 @@ solid_infill_speed = 40
|
|||
top_infill_extrusion_width = 0.45
|
||||
top_solid_infill_speed = 30
|
||||
|
||||
[print:0.15mm OPTIMAL MK3 SOLUBLE INTERFACE]
|
||||
inherits = 0.15mm OPTIMAL MK3 SOLUBLE FULL
|
||||
notes = Set your solluble extruder in Multiple Extruders > Support material/raft interface extruder
|
||||
# MK3 MMU #
|
||||
[print:0.15mm SOLUBLE INTERFACE MK3]
|
||||
inherits = 0.15mm SOLUBLE FULL MK3
|
||||
notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder
|
||||
support_material_extruder = 0
|
||||
support_material_interface_layers = 3
|
||||
support_material_with_sheath = 0
|
||||
|
||||
# MK2 MMU #
|
||||
[print:0.15mm OPTIMAL SOLUBLE FULL]
|
||||
inherits = *0.15mm*; *soluble_support*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 and num_extruders>1
|
||||
external_perimeter_speed = 25
|
||||
notes = Set your solluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder
|
||||
notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder
|
||||
perimeter_speed = 40
|
||||
solid_infill_speed = 40
|
||||
top_infill_extrusion_width = 0.45
|
||||
top_solid_infill_speed = 30
|
||||
|
||||
# MK2 MMU #
|
||||
[print:0.15mm OPTIMAL SOLUBLE INTERFACE]
|
||||
inherits = 0.15mm OPTIMAL SOLUBLE FULL
|
||||
notes = Set your solluble extruder in Multiple Extruders > Support material/raft interface extruder
|
||||
notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder
|
||||
support_material_extruder = 0
|
||||
support_material_interface_layers = 3
|
||||
support_material_with_sheath = 0
|
||||
support_material_xy_spacing = 80%
|
||||
|
||||
# MK3 #
|
||||
[print:0.15mm OPTIMAL 0.25 nozzle MK3]
|
||||
inherits = *0.15mm*; *0.25nozzle*; *MK3*
|
||||
bridge_speed = 30
|
||||
|
|
@ -451,6 +557,7 @@ solid_infill_speed = 50
|
|||
top_infill_extrusion_width = 0.4
|
||||
top_solid_layers = 5
|
||||
|
||||
# MK3 #
|
||||
[print:0.15mm OPTIMAL 0.6 nozzle MK3]
|
||||
inherits = *0.15mm*; *0.6nozzle*; *MK3*
|
||||
bridge_speed = 30
|
||||
|
|
@ -467,6 +574,7 @@ top_solid_infill_speed = 50
|
|||
# XXX--- 0.20mm ---XXX
|
||||
# XXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
# MK2 #
|
||||
[print:0.20mm 100mms Linear Advance]
|
||||
inherits = *0.20mm*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4
|
||||
|
|
@ -479,7 +587,22 @@ solid_infill_speed = 100
|
|||
support_material_speed = 60
|
||||
top_solid_infill_speed = 70
|
||||
|
||||
[print:0.20mm FAST MK3]
|
||||
# MK3 #
|
||||
[print:0.20mm QUALITY MK3]
|
||||
inherits = *0.20mm*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
|
||||
external_perimeter_speed = 25
|
||||
infill_acceleration = 1250
|
||||
infill_speed = 80
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
solid_infill_speed = 80
|
||||
top_solid_infill_speed = 40
|
||||
fill_pattern = gyroid
|
||||
fill_density = 15%
|
||||
|
||||
[print:0.20mm SPEED MK3]
|
||||
inherits = *0.20mm*; *MK3*
|
||||
bridge_speed = 30
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4
|
||||
|
|
@ -487,14 +610,15 @@ external_perimeter_speed = 35
|
|||
infill_acceleration = 1250
|
||||
infill_speed = 200
|
||||
max_print_speed = 200
|
||||
perimeter_speed = 45
|
||||
perimeter_speed = 60
|
||||
solid_infill_speed = 200
|
||||
top_solid_infill_speed = 50
|
||||
|
||||
[print:0.20mm FAST MK3 SOLUBLE FULL]
|
||||
inherits = 0.20mm FAST MK3; *soluble_support*
|
||||
# MK3 MMU #
|
||||
[print:0.20mm SOLUBLE FULL MK3]
|
||||
inherits = 0.20mm SPEED MK3; *soluble_support*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and nozzle_diameter[0]==0.4 and num_extruders>1
|
||||
notes = Set your solluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder
|
||||
notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder
|
||||
support_material_extruder = 5
|
||||
support_material_interface_extruder = 5
|
||||
perimeter_speed = 40
|
||||
|
|
@ -502,38 +626,44 @@ solid_infill_speed = 40
|
|||
top_infill_extrusion_width = 0.45
|
||||
top_solid_infill_speed = 30
|
||||
|
||||
[print:0.20mm FAST MK3 SOLUBLE INTERFACE]
|
||||
inherits = 0.20mm FAST MK3 SOLUBLE FULL
|
||||
notes = Set your solluble extruder in Multiple Extruders > Support material/raft interface extruder
|
||||
# MK3 MMU #
|
||||
[print:0.20mm SOLUBLE INTERFACE MK3]
|
||||
inherits = 0.20mm SOLUBLE FULL MK3
|
||||
notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder
|
||||
support_material_extruder = 0
|
||||
support_material_interface_layers = 3
|
||||
support_material_with_sheath = 0
|
||||
|
||||
# MK2 #
|
||||
[print:0.20mm NORMAL]
|
||||
inherits = *0.20mm*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4
|
||||
|
||||
# MK2 #
|
||||
[print:0.20mm NORMAL 0.6 nozzle]
|
||||
inherits = *0.20mm*; *0.6nozzle*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6
|
||||
|
||||
# MK2 MMU #
|
||||
[print:0.20mm NORMAL SOLUBLE FULL]
|
||||
inherits = *0.20mm*; *soluble_support*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2[^\.].*/ and nozzle_diameter[0]==0.4 and num_extruders>1
|
||||
external_perimeter_speed = 30
|
||||
notes = Set your solluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder
|
||||
notes = Set your soluble extruder in Multiple Extruders > Support material/raft/skirt extruder & Support material/raft interface extruder
|
||||
perimeter_speed = 40
|
||||
solid_infill_speed = 40
|
||||
top_solid_infill_speed = 30
|
||||
|
||||
# MK2 MMU #
|
||||
[print:0.20mm NORMAL SOLUBLE INTERFACE]
|
||||
inherits = 0.20mm NORMAL SOLUBLE FULL
|
||||
notes = Set your solluble extruder in Multiple Extruders > Support material/raft interface extruder
|
||||
notes = Set your soluble extruder in Multiple Extruders > Support material/raft interface extruder
|
||||
support_material_extruder = 0
|
||||
support_material_interface_layers = 3
|
||||
support_material_with_sheath = 0
|
||||
support_material_xy_spacing = 80%
|
||||
|
||||
# MK3 #
|
||||
[print:0.20mm FAST 0.6 nozzle MK3]
|
||||
inherits = *0.20mm*; *0.6nozzle*; *MK3*
|
||||
bridge_speed = 30
|
||||
|
|
@ -567,6 +697,7 @@ solid_infill_speed = 60
|
|||
top_solid_infill_speed = 50
|
||||
top_solid_layers = 4
|
||||
|
||||
# MK2 #
|
||||
[print:0.35mm FAST]
|
||||
inherits = *0.35mm*
|
||||
bridge_flow_ratio = 0.95
|
||||
|
|
@ -576,10 +707,12 @@ perimeter_extrusion_width = 0.43
|
|||
solid_infill_extrusion_width = 0.7
|
||||
top_infill_extrusion_width = 0.43
|
||||
|
||||
# MK2 #
|
||||
[print:0.35mm FAST 0.6 nozzle]
|
||||
inherits = *0.35mm*; *0.6nozzle*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6
|
||||
|
||||
# MK2 MMU #
|
||||
[print:0.35mm FAST sol full 0.6 nozzle]
|
||||
inherits = *0.35mm*; *0.6nozzle*; *soluble_support*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 and num_extruders>1
|
||||
|
|
@ -591,6 +724,7 @@ support_material_interface_layers = 3
|
|||
support_material_xy_spacing = 120%
|
||||
top_infill_extrusion_width = 0.57
|
||||
|
||||
# MK2 MMU #
|
||||
[print:0.35mm FAST sol int 0.6 nozzle]
|
||||
inherits = 0.35mm FAST sol full 0.6 nozzle
|
||||
support_material_extruder = 0
|
||||
|
|
@ -602,38 +736,45 @@ support_material_xy_spacing = 150%
|
|||
# XXX----- MK2.5 ----XXX
|
||||
# XXXXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
# MK2.5 #
|
||||
[print:0.15mm 100mms Linear Advance MK2.5]
|
||||
inherits = 0.15mm 100mms Linear Advance
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4
|
||||
single_extruder_multi_material_priming = 0
|
||||
|
||||
# MK2.5 #
|
||||
[print:0.15mm OPTIMAL MK2.5]
|
||||
inherits = 0.15mm OPTIMAL
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4
|
||||
single_extruder_multi_material_priming = 0
|
||||
|
||||
# MK2.5 MMU2 #
|
||||
[print:0.15mm OPTIMAL SOLUBLE FULL MK2.5]
|
||||
inherits = 0.15mm OPTIMAL SOLUBLE FULL
|
||||
support_material_extruder = 5
|
||||
support_material_interface_extruder = 5
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1
|
||||
|
||||
# MK2.5 MMU2 #
|
||||
[print:0.15mm OPTIMAL SOLUBLE INTERFACE MK2.5]
|
||||
inherits = 0.15mm OPTIMAL SOLUBLE INTERFACE
|
||||
support_material_extruder = 0
|
||||
support_material_interface_extruder = 5
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1
|
||||
|
||||
# MK2.5 #
|
||||
[print:0.20mm 100mms Linear Advance MK2.5]
|
||||
inherits = 0.20mm 100mms Linear Advance
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4
|
||||
single_extruder_multi_material_priming = 0
|
||||
|
||||
# MK2.5 #
|
||||
[print:0.20mm NORMAL MK2.5]
|
||||
inherits = 0.20mm NORMAL
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4
|
||||
single_extruder_multi_material_priming = 0
|
||||
|
||||
# MK2.5 MMU2 #
|
||||
[print:0.20mm NORMAL SOLUBLE FULL MK2.5]
|
||||
inherits = 0.20mm NORMAL SOLUBLE FULL
|
||||
support_material_extruder = 5
|
||||
|
|
@ -641,6 +782,7 @@ support_material_interface_extruder = 5
|
|||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1
|
||||
single_extruder_multi_material_priming = 0
|
||||
|
||||
# MK2.5 MMU2 #
|
||||
[print:0.20mm NORMAL SOLUBLE INTERFACE MK2.5]
|
||||
inherits = 0.20mm NORMAL SOLUBLE INTERFACE
|
||||
support_material_extruder = 0
|
||||
|
|
@ -648,6 +790,7 @@ support_material_interface_extruder = 5
|
|||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4 and num_extruders>1
|
||||
single_extruder_multi_material_priming = 0
|
||||
|
||||
# MK2.5 #
|
||||
[print:0.35mm FAST MK2.5]
|
||||
inherits = 0.35mm FAST
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.5.*/ and nozzle_diameter[0]==0.4
|
||||
|
|
@ -934,7 +1077,7 @@ first_layer_bed_temperature = 100
|
|||
first_layer_temperature = 270
|
||||
temperature = 270
|
||||
|
||||
[filament:Primavalue PVA]
|
||||
[filament:PrimaSelect PVA+]
|
||||
inherits = *PLA*
|
||||
filament_cost = 108
|
||||
filament_density = 1.23
|
||||
|
|
@ -963,6 +1106,11 @@ filament_cooling_final_speed = 50
|
|||
filament_cooling_initial_speed = 10
|
||||
filament_cooling_moves = 5
|
||||
filament_ramming_parameters = "120 110 5.32258 5.45161 5.67742 6 6.48387 7.12903 7.90323 8.70968 9.3871 9.83871 10.0968 10.2258| 0.05 5.30967 0.45 5.50967 0.95 6.1871 1.45 7.39677 1.95 9.05484 2.45 10 2.95 10.3098 3.45 13.0839 3.95 7.6 4.45 7.6 4.95 7.6";
|
||||
filament_loading_speed_start = 19
|
||||
filament_load_time = 15
|
||||
filament_unload_time = 12
|
||||
filament_loading_speed = 14
|
||||
filament_unloading_speed = 20
|
||||
|
||||
[filament:Generic ABS MMU2]
|
||||
inherits = *ABS MMU2*
|
||||
|
|
@ -1009,13 +1157,14 @@ first_layer_temperature = 230
|
|||
filament_cooling_final_speed = 1
|
||||
filament_cooling_initial_speed = 2
|
||||
filament_cooling_moves = 1
|
||||
filament_load_time = 12
|
||||
filament_load_time = 15
|
||||
filament_loading_speed = 14
|
||||
filament_notes = PET
|
||||
filament_ramming_parameters = "120 140 4.70968 4.74194 4.77419 4.80645 4.83871 4.87097 4.90323 5 5.25806 5.67742 6.29032 7.06452 7.83871 8.3871| 0.05 4.72901 0.45 4.73545 0.95 4.83226 1.45 4.88067 1.95 5.05483 2.45 5.93553 2.95 7.53556 3.45 8.6323 3.95 7.6 4.45 7.6 4.95 7.6"
|
||||
filament_unload_time = 11
|
||||
filament_unload_time = 12
|
||||
filament_unloading_speed = 20
|
||||
filament_unloading_speed_start = 120
|
||||
filament_loading_speed_start = 19
|
||||
|
||||
[filament:Generic PET MMU2]
|
||||
inherits = *PET MMU2*
|
||||
|
|
@ -1023,7 +1172,7 @@ inherits = *PET MMU2*
|
|||
[filament:Prusa PET MMU2]
|
||||
inherits = *PET MMU2*
|
||||
|
||||
[filament:Prusament PET MMU2]
|
||||
[filament:Prusament PETG MMU2]
|
||||
inherits = *PET MMU2*
|
||||
|
||||
[filament:Prusa PLA]
|
||||
|
|
@ -1043,14 +1192,17 @@ filament_notes = "Affordable filament for everyday printing in premium quality m
|
|||
inherits = Prusa PLA
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material
|
||||
temperature = 205
|
||||
filament_cooling_final_speed = 1
|
||||
filament_cooling_initial_speed = 2
|
||||
filament_cooling_final_speed = 2
|
||||
filament_cooling_initial_speed = 3
|
||||
filament_cooling_moves = 1
|
||||
filament_load_time = 12
|
||||
filament_load_time = 15
|
||||
filament_loading_speed = 14
|
||||
filament_ramming_parameters = "120 110 2.70968 2.93548 3.32258 3.83871 4.58065 5.54839 6.51613 7.35484 7.93548 8.16129| 0.05 2.66451 0.45 3.05805 0.95 4.05807 1.45 5.97742 1.95 7.69999 2.45 8.1936 2.95 11.342 3.45 11.4065 3.95 7.6 4.45 7.6 4.95 7.6"
|
||||
filament_unload_time = 11
|
||||
filament_ramming_parameters = "130 120 2.70968 2.93548 3.32258 3.83871 4.58065 5.54839 6.51613 7.35484 7.93548 8.16129| 0.05 2.66451 0.45 3.05805 0.95 4.05807 1.45 5.97742 1.95 7.69999 2.45 8.1936 2.95 11.342 3.45 11.4065 3.95 7.6 4.45 7.6 4.95 7.6"
|
||||
filament_unload_time = 12
|
||||
filament_unloading_speed = 20
|
||||
filament_loading_speed_start = 19
|
||||
filament_minimal_purge_on_wipe_tower = 15
|
||||
filament_unloading_speed_start = 100
|
||||
|
||||
[filament:Generic PLA MMU2]
|
||||
inherits = *PLA MMU2*
|
||||
|
|
@ -1133,14 +1285,53 @@ first_layer_temperature = 200
|
|||
filament_cooling_final_speed = 1
|
||||
filament_cooling_initial_speed = 2
|
||||
filament_max_volumetric_speed = 4
|
||||
filament_type = PVA
|
||||
filament_type = PLA
|
||||
filament_cooling_moves = 1
|
||||
filament_load_time = 12
|
||||
filament_load_time = 15
|
||||
filament_loading_speed = 14
|
||||
filament_ramming_parameters = "120 110 1.74194 1.90323 2.16129 2.48387 2.83871 3.25806 3.83871 4.6129 5.41935 5.96774| 0.05 1.69677 0.45 1.96128 0.95 2.63872 1.45 3.46129 1.95 4.99031 2.45 6.12908 2.95 8.30974 3.45 11.4065 3.95 7.6 4.45 7.6 4.95 7.6"
|
||||
filament_unload_time = 11
|
||||
filament_unload_time = 12
|
||||
filament_unloading_speed = 20
|
||||
filament_unloading_speed_start = 100
|
||||
filament_loading_speed_start = 19
|
||||
|
||||
[filament:PrimaSelect PVA+ MMU2]
|
||||
inherits = *common*
|
||||
compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material
|
||||
bed_temperature = 60
|
||||
bridge_fan_speed = 100
|
||||
cooling = 0
|
||||
disable_fan_first_layers = 1
|
||||
fan_always_on = 0
|
||||
fan_below_layer_time = 100
|
||||
filament_colour = #FFFFD7
|
||||
filament_cooling_final_speed = 2
|
||||
filament_cooling_initial_speed = 4
|
||||
filament_cooling_moves = 2
|
||||
filament_cost = 25.4
|
||||
filament_density = 1.24
|
||||
filament_diameter = 1.75
|
||||
filament_load_time = 15
|
||||
filament_loading_speed = 14
|
||||
filament_loading_speed_start = 19
|
||||
filament_max_volumetric_speed = 4
|
||||
filament_minimal_purge_on_wipe_tower = 5
|
||||
filament_notes = PVA
|
||||
filament_ramming_parameters = "120 110 3.83871 3.90323 3.96774 4.03226 4.09677 4.19355 4.3871 4.83871 5.67742 6.93548 8.54839 10.3226 11.9677 13.2581 14.129 14.5806| 0.05 3.8258 0.45 3.89676 0.95 4.05807 1.45 4.23548 1.95 5.18386 2.45 7.80651 2.95 11.5356 3.45 13.9872 3.95 14.7613 4.45 7.6 4.95 7.6"
|
||||
filament_soluble = 1
|
||||
filament_toolchange_delay = 0
|
||||
filament_type = PLA
|
||||
filament_unload_time = 12
|
||||
filament_unloading_speed = 20
|
||||
filament_unloading_speed_start = 100
|
||||
first_layer_bed_temperature = 60
|
||||
first_layer_temperature = 200
|
||||
max_fan_speed = 100
|
||||
min_fan_speed = 100
|
||||
min_print_speed = 15
|
||||
slowdown_below_layer_time = 20
|
||||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}30{endif}; Filament gcode"
|
||||
temperature = 195
|
||||
|
||||
[filament:Verbatim PP]
|
||||
inherits = *common*
|
||||
|
|
@ -1322,33 +1513,66 @@ inherits = *common 0.05*
|
|||
exposure_time = 7
|
||||
initial_exposure_time = 40
|
||||
|
||||
# v2
|
||||
|
||||
[sla_material:3DM-ABS 0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 9
|
||||
initial_exposure_time = 35
|
||||
|
||||
[sla_material:3DM-DENT 0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 7
|
||||
initial_exposure_time = 45
|
||||
|
||||
[sla_material:3DM-HR Green 0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 15
|
||||
initial_exposure_time = 40
|
||||
|
||||
[sla_material:3DM-HR Red Wine 0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 9
|
||||
initial_exposure_time = 35
|
||||
|
||||
[sla_material:3DM-XPRO White 0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 9
|
||||
initial_exposure_time = 35
|
||||
|
||||
[sla_material:FTD Ash Grey 0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 9
|
||||
initial_exposure_time = 40
|
||||
|
||||
[sla_material:Jamg He LOC-19 Super Low Odor Skin 0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 7.5
|
||||
initial_exposure_time = 40
|
||||
|
||||
[sla_material:Jamg He LOC-20 Super Low Odor White 0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 6.5
|
||||
initial_exposure_time = 40
|
||||
|
||||
[sla_material:Jamg He LOC-60 Super Low Odor Grey 0.05]
|
||||
inherits = *common 0.05*
|
||||
exposure_time = 6.5
|
||||
initial_exposure_time = 40
|
||||
|
||||
########### Materials 0.035
|
||||
|
||||
## [sla_material:Jamg He Transparent Clear 0.035]
|
||||
## inherits = *common 0.035*
|
||||
|
||||
## [sla_material:Jamg He Transparent Green 0.035]
|
||||
## inherits = *common 0.035*
|
||||
|
||||
## [sla_material:Jamg He Transparent Orange 0.035]
|
||||
## inherits = *common 0.035*
|
||||
|
||||
## [sla_material:Jamg He Transparent Red 0.035]
|
||||
## inherits = *common 0.035*
|
||||
[sla_material:Jamg He PJHC-30 Orange 0.035]
|
||||
inherits = *common 0.035*
|
||||
exposure_time = 9
|
||||
initial_exposure_time = 35
|
||||
|
||||
########### Materials 0.1
|
||||
|
||||
## [sla_material:Jamg He Transparent Clear 0.1]
|
||||
## inherits = *common 0.1*
|
||||
|
||||
## [sla_material:Jamg He Transparent Green 0.1]
|
||||
## inherits = *common 0.1*
|
||||
|
||||
## [sla_material:Jamg He Transparent Orange 0.1]
|
||||
## inherits = *common 0.1*
|
||||
|
||||
## [sla_material:Jamg He Transparent Red 0.1]
|
||||
## inherits = *common 0.1*
|
||||
[sla_material:Jamg He PJHC-30 Orange 0.1]
|
||||
inherits = *common 0.1*
|
||||
exposure_time = 10
|
||||
initial_exposure_time = 45
|
||||
|
||||
[printer:*common*]
|
||||
printer_technology = FFF
|
||||
|
|
@ -1455,10 +1679,10 @@ default_print_profile = 0.15mm OPTIMAL
|
|||
# XXX--- MK2 ---XXX
|
||||
# XXXXXXXXXXXXXXXXX
|
||||
|
||||
[printer:Original Prusa i3 MK2]
|
||||
[printer:Original Prusa i3 MK2S]
|
||||
inherits = *common*
|
||||
|
||||
[printer:Original Prusa i3 MK2 0.25 nozzle]
|
||||
[printer:Original Prusa i3 MK2S 0.25 nozzle]
|
||||
inherits = *common*
|
||||
max_layer_height = 0.15
|
||||
min_layer_height = 0.05
|
||||
|
|
@ -1469,7 +1693,7 @@ variable_layer_height = 1
|
|||
printer_variant = 0.25
|
||||
default_print_profile = 0.10mm DETAIL 0.25 nozzle
|
||||
|
||||
[printer:Original Prusa i3 MK2 0.6 nozzle]
|
||||
[printer:Original Prusa i3 MK2S 0.6 nozzle]
|
||||
inherits = *common*
|
||||
max_layer_height = 0.35
|
||||
min_layer_height = 0.1
|
||||
|
|
@ -1481,12 +1705,12 @@ default_print_profile = 0.20mm NORMAL 0.6 nozzle
|
|||
# XXX--- MK2MM ---XXX
|
||||
# XXXXXXXXXXXXXXXXXXX
|
||||
|
||||
[printer:Original Prusa i3 MK2 MMU1 Single]
|
||||
[printer:Original Prusa i3 MK2S MMU1 Single]
|
||||
inherits = *mm-single*
|
||||
max_layer_height = 0.25
|
||||
min_layer_height = 0.07
|
||||
|
||||
[printer:Original Prusa i3 MK2 MMU1 Single 0.6 nozzle]
|
||||
[printer:Original Prusa i3 MK2S MMU1 Single 0.6 nozzle]
|
||||
inherits = *mm-single*
|
||||
nozzle_diameter = 0.6
|
||||
printer_variant = 0.6
|
||||
|
|
@ -1494,13 +1718,13 @@ default_print_profile = 0.20mm NORMAL 0.6 nozzle
|
|||
max_layer_height = 0.35
|
||||
min_layer_height = 0.1
|
||||
|
||||
[printer:Original Prusa i3 MK2 MMU1]
|
||||
[printer:Original Prusa i3 MK2S MMU1]
|
||||
inherits = *mm-multi*
|
||||
nozzle_diameter = 0.4,0.4,0.4,0.4
|
||||
max_layer_height = 0.25
|
||||
min_layer_height = 0.07
|
||||
|
||||
[printer:Original Prusa i3 MK2 MMU1 0.6 nozzle]
|
||||
[printer:Original Prusa i3 MK2S MMU1 0.6 nozzle]
|
||||
inherits = *mm-multi*
|
||||
nozzle_diameter = 0.6,0.6,0.6,0.6
|
||||
printer_variant = 0.6
|
||||
|
|
@ -1513,7 +1737,19 @@ min_layer_height = 0.1
|
|||
# XXXXXXXXXXXXXXXXXXX
|
||||
|
||||
[printer:Original Prusa i3 MK2.5]
|
||||
inherits = Original Prusa i3 MK2
|
||||
inherits = Original Prusa i3 MK2S
|
||||
printer_model = MK2.5
|
||||
remaining_times = 1
|
||||
start_gcode = M115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
|
||||
[printer:Original Prusa i3 MK2.5 0.25 nozzle]
|
||||
inherits = Original Prusa i3 MK2S 0.25 nozzle
|
||||
printer_model = MK2.5
|
||||
remaining_times = 1
|
||||
start_gcode = M115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
|
||||
[printer:Original Prusa i3 MK2.5 0.6 nozzle]
|
||||
inherits = Original Prusa i3 MK2S 0.6 nozzle
|
||||
printer_model = MK2.5
|
||||
remaining_times = 1
|
||||
start_gcode = M115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
|
|
@ -1545,7 +1781,7 @@ machine_min_travel_rate = 0
|
|||
default_print_profile = 0.15mm OPTIMAL MK2.5
|
||||
default_filament_profile = Prusament PLA
|
||||
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n
|
||||
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
|
||||
|
||||
[printer:Original Prusa i3 MK2.5 MMU2]
|
||||
|
|
@ -1579,20 +1815,85 @@ single_extruder_multi_material = 1
|
|||
# to be defined explicitely.
|
||||
nozzle_diameter = 0.4,0.4,0.4,0.4,0.4
|
||||
extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F
|
||||
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n
|
||||
|
||||
[printer:Original Prusa i3 MK2.5 0.25 nozzle]
|
||||
inherits = Original Prusa i3 MK2 0.25 nozzle
|
||||
printer_model = MK2.5
|
||||
remaining_times = 1
|
||||
start_gcode = M115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
[printer:Original Prusa i3 MK2.5S]
|
||||
inherits = Original Prusa i3 MK2.5
|
||||
printer_model = MK2.5S
|
||||
|
||||
[printer:Original Prusa i3 MK2.5 0.6 nozzle]
|
||||
inherits = Original Prusa i3 MK2 0.6 nozzle
|
||||
printer_model = MK2.5
|
||||
[printer:Original Prusa i3 MK2.5S 0.25 nozzle]
|
||||
inherits = Original Prusa i3 MK2.5 0.25 nozzle
|
||||
printer_model = MK2.5S
|
||||
|
||||
[printer:Original Prusa i3 MK2.5S 0.6 nozzle]
|
||||
inherits = Original Prusa i3 MK2.5 0.6 nozzle
|
||||
printer_model = MK2.5S
|
||||
|
||||
[printer:Original Prusa i3 MK2.5S MMU2S Single]
|
||||
inherits = Original Prusa i3 MK2.5; *mm2s*
|
||||
printer_model = MK2.5SMMU2S
|
||||
single_extruder_multi_material = 0
|
||||
max_print_height = 200
|
||||
remaining_times = 1
|
||||
start_gcode = M115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0
|
||||
silent_mode = 0
|
||||
retract_lift_below = 199
|
||||
machine_max_acceleration_e = 10000
|
||||
machine_max_acceleration_extruding = 2000
|
||||
machine_max_acceleration_retracting = 1500
|
||||
machine_max_acceleration_x = 9000
|
||||
machine_max_acceleration_y = 9000
|
||||
machine_max_acceleration_z = 500
|
||||
machine_max_feedrate_e = 120
|
||||
machine_max_feedrate_x = 500
|
||||
machine_max_feedrate_y = 500
|
||||
machine_max_feedrate_z = 12
|
||||
machine_max_jerk_e = 2.5
|
||||
machine_max_jerk_x = 10
|
||||
machine_max_jerk_y = 10
|
||||
machine_max_jerk_z = 0.2
|
||||
machine_min_extruding_rate = 0
|
||||
machine_min_travel_rate = 0
|
||||
default_print_profile = 0.15mm OPTIMAL MK2.5
|
||||
default_filament_profile = Prusament PLA
|
||||
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n
|
||||
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
|
||||
|
||||
[printer:Original Prusa i3 MK2.5S MMU2S]
|
||||
inherits = Original Prusa i3 MK2.5; *mm2s*
|
||||
printer_model = MK2.5SMMU2S
|
||||
max_print_height = 200
|
||||
remaining_times = 1
|
||||
silent_mode = 0
|
||||
retract_lift_below = 199
|
||||
machine_max_acceleration_e = 10000
|
||||
machine_max_acceleration_extruding = 2000
|
||||
machine_max_acceleration_retracting = 1500
|
||||
machine_max_acceleration_x = 9000
|
||||
machine_max_acceleration_y = 9000
|
||||
machine_max_acceleration_z = 500
|
||||
machine_max_feedrate_e = 120
|
||||
machine_max_feedrate_x = 500
|
||||
machine_max_feedrate_y = 500
|
||||
machine_max_feedrate_z = 12
|
||||
machine_max_jerk_e = 2.5
|
||||
machine_max_jerk_x = 10
|
||||
machine_max_jerk_y = 10
|
||||
machine_max_jerk_z = 0.2
|
||||
machine_min_extruding_rate = 0
|
||||
machine_min_travel_rate = 0
|
||||
default_print_profile = 0.15mm OPTIMAL MK2.5
|
||||
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK2.5\n
|
||||
single_extruder_multi_material = 1
|
||||
# The 5x nozzle diameter defines the number of extruders. Other extruder parameters
|
||||
# (for example the retract values) are duplicaed from the first value, so they do not need
|
||||
# to be defined explicitely.
|
||||
nozzle_diameter = 0.4,0.4,0.4,0.4,0.4
|
||||
extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F
|
||||
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n
|
||||
|
||||
|
||||
# XXXXXXXXXXXXXXXXX
|
||||
# XXX--- MK3 ---XXX
|
||||
|
|
@ -1622,9 +1923,9 @@ remaining_times = 1
|
|||
printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MK3\n
|
||||
retract_lift_below = 209
|
||||
max_print_height = 210
|
||||
start_gcode = M115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height==0.05}100{else}95{endif}
|
||||
start_gcode = M115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG1 Y-3.0 F1000.0 ; go outside print area\nG92 E0.0\nG1 X60.0 E9.0 F1000.0 ; intro line\nG1 X100.0 E12.5 F1000.0 ; intro line\nG92 E0.0\nM221 S{if layer_height<0.075}100{else}95{endif}
|
||||
printer_model = MK3
|
||||
default_print_profile = 0.15mm OPTIMAL MK3
|
||||
default_print_profile = 0.15mm QUALITY MK3
|
||||
|
||||
[printer:Original Prusa i3 MK3 0.25 nozzle]
|
||||
inherits = Original Prusa i3 MK3
|
||||
|
|
@ -1642,6 +1943,18 @@ min_layer_height = 0.1
|
|||
printer_variant = 0.6
|
||||
default_print_profile = 0.15mm OPTIMAL 0.6 nozzle MK3
|
||||
|
||||
[printer:Original Prusa i3 MK3S]
|
||||
inherits = Original Prusa i3 MK3
|
||||
printer_model = MK3S
|
||||
|
||||
[printer:Original Prusa i3 MK3S 0.25 nozzle]
|
||||
inherits = Original Prusa i3 MK3 0.25 nozzle
|
||||
printer_model = MK3S
|
||||
|
||||
[printer:Original Prusa i3 MK3S 0.6 nozzle]
|
||||
inherits = Original Prusa i3 MK3 0.6 nozzle
|
||||
printer_model = MK3S
|
||||
|
||||
[printer:*mm2*]
|
||||
inherits = Original Prusa i3 MK3
|
||||
single_extruder_multi_material = 1
|
||||
|
|
@ -1651,7 +1964,19 @@ parking_pos_retraction = 85
|
|||
retract_length_toolchange = 3
|
||||
extra_loading_move = -13
|
||||
printer_model = MK3MMU2
|
||||
default_print_profile = 0.15mm OPTIMAL MK3
|
||||
default_print_profile = 0.15mm QUALITY MK3
|
||||
default_filament_profile = Prusament PLA MMU2
|
||||
|
||||
[printer:*mm2s*]
|
||||
inherits = Original Prusa i3 MK3
|
||||
single_extruder_multi_material = 1
|
||||
cooling_tube_length = 20
|
||||
cooling_tube_retraction = 40
|
||||
parking_pos_retraction = 85
|
||||
retract_length_toolchange = 3
|
||||
extra_loading_move = -25
|
||||
printer_model = MK3SMMU2S
|
||||
default_print_profile = 0.15mm QUALITY MK3
|
||||
default_filament_profile = Prusament PLA MMU2
|
||||
|
||||
[printer:Original Prusa i3 MK3 MMU2 Single]
|
||||
|
|
@ -1672,6 +1997,21 @@ extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F
|
|||
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n
|
||||
|
||||
[printer:Original Prusa i3 MK3S MMU2S Single]
|
||||
inherits = *mm2s*
|
||||
single_extruder_multi_material = 0
|
||||
default_filament_profile = Prusament PLA
|
||||
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nTx\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\n\nG21 ; set units to millimeters\n\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nTc\n; purge line\nG1 X55.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
end_gcode = G1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X0 Y200; home X axis\nM84 ; disable motors
|
||||
|
||||
[printer:Original Prusa i3 MK3S MMU2S]
|
||||
inherits = *mm2s*
|
||||
machine_max_acceleration_e = 8000,8000
|
||||
nozzle_diameter = 0.4,0.4,0.4,0.4,0.4
|
||||
extruder_colour = #FF8000;#DB5182;#00FFFF;#FF4F4F;#9FFF9F
|
||||
start_gcode = M107\nM115 U3.5.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E29.0 F1073.0\nG1 X5.0 E29.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n
|
||||
|
||||
[printer:Original Prusa SL1]
|
||||
printer_technology = SLA
|
||||
printer_model = SL1
|
||||
|
|
|
|||
20
resources/shaders/printbed.fs
Normal file
20
resources/shaders/printbed.fs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#version 110
|
||||
|
||||
const vec3 back_color_dark = vec3(0.235, 0.235, 0.235);
|
||||
const vec3 back_color_light = vec3(0.365, 0.365, 0.365);
|
||||
|
||||
uniform sampler2D texture;
|
||||
uniform bool transparent_background;
|
||||
|
||||
varying vec2 tex_coords;
|
||||
|
||||
void main()
|
||||
{
|
||||
// calculates radial gradient
|
||||
vec3 back_color = vec3(mix(back_color_light, back_color_dark, smoothstep(0.0, 0.5, length(abs(tex_coords.xy) - vec2(0.5)))));
|
||||
|
||||
vec4 fore_color = texture2D(texture, tex_coords);
|
||||
|
||||
// blends foreground with background
|
||||
gl_FragColor = vec4(mix(back_color, fore_color.rgb, fore_color.a), transparent_background ? fore_color.a : 1.0);
|
||||
}
|
||||
11
resources/shaders/printbed.vs
Normal file
11
resources/shaders/printbed.vs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#version 110
|
||||
|
||||
attribute vec2 v_tex_coords;
|
||||
|
||||
varying vec2 tex_coords;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = ftransform();
|
||||
tex_coords = v_tex_coords;
|
||||
}
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
add_executable(slabasebed EXCLUDE_FROM_ALL slabasebed.cpp)
|
||||
target_link_libraries(slabasebed libslic3r)
|
||||
target_link_libraries(slabasebed libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS})
|
||||
|
|
|
|||
|
|
@ -1,15 +1,29 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include <libslic3r/libslic3r.h>
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
#include <libslic3r/SLA/SLABasePool.hpp>
|
||||
#include <libslic3r/SLA/SLABoilerPlate.hpp>
|
||||
#include <libnest2d/tools/benchmark.h>
|
||||
|
||||
const std::string USAGE_STR = {
|
||||
"Usage: slabasebed stlfilename.stl"
|
||||
};
|
||||
|
||||
namespace Slic3r { namespace sla {
|
||||
|
||||
Contour3D convert(const Polygons& triangles, coord_t z, bool dir);
|
||||
Contour3D walls(const Polygon& floor_plate, const Polygon& ceiling,
|
||||
double floor_z_mm, double ceiling_z_mm,
|
||||
double offset_difference_mm, ThrowOnCancel thr);
|
||||
|
||||
void offset(ExPolygon& sh, coord_t distance);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int main(const int argc, const char *argv[]) {
|
||||
using namespace Slic3r;
|
||||
using std::cout; using std::endl;
|
||||
|
|
@ -26,18 +40,43 @@ int main(const int argc, const char *argv[]) {
|
|||
model.align_to_origin();
|
||||
|
||||
ExPolygons ground_slice;
|
||||
TriangleMesh basepool;
|
||||
sla::Contour3D mesh;
|
||||
// TriangleMesh basepool;
|
||||
|
||||
sla::base_plate(model, ground_slice, 0.1f);
|
||||
|
||||
if(ground_slice.empty()) return EXIT_FAILURE;
|
||||
|
||||
ExPolygon bottom_plate = ground_slice.front();
|
||||
ExPolygon top_plate = bottom_plate;
|
||||
sla::offset(top_plate, coord_t(3.0/SCALING_FACTOR));
|
||||
sla::offset(bottom_plate, coord_t(1.0/SCALING_FACTOR));
|
||||
|
||||
bench.start();
|
||||
sla::create_base_pool(ground_slice, basepool);
|
||||
|
||||
Polygons top_plate_triangles, bottom_plate_triangles;
|
||||
top_plate.triangulate_p2t(&top_plate_triangles);
|
||||
bottom_plate.triangulate_p2t(&bottom_plate_triangles);
|
||||
|
||||
auto top_plate_mesh = sla::convert(top_plate_triangles, coord_t(3.0/SCALING_FACTOR), false);
|
||||
auto bottom_plate_mesh = sla::convert(bottom_plate_triangles, 0, true);
|
||||
|
||||
mesh.merge(bottom_plate_mesh);
|
||||
mesh.merge(top_plate_mesh);
|
||||
|
||||
sla::Contour3D w = sla::walls(bottom_plate.contour, top_plate.contour, 0, 3, 2.0, [](){});
|
||||
|
||||
mesh.merge(w);
|
||||
// sla::create_base_pool(ground_slice, basepool);
|
||||
bench.stop();
|
||||
|
||||
cout << "Base pool creation time: " << std::setprecision(10)
|
||||
<< bench.getElapsedSec() << " seconds." << endl;
|
||||
|
||||
basepool.write_ascii("out.stl");
|
||||
// basepool.write_ascii("out.stl");
|
||||
|
||||
std::fstream outstream("out.obj", std::fstream::out);
|
||||
mesh.to_obj(outstream);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,22 +169,22 @@
|
|||
#define LT_OBJDIR ".libs/"
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "avrdude"
|
||||
#define PACKAGE "avrdude-slic3r"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT "avrdude-dev@nongnu.org"
|
||||
#define PACKAGE_BUGREPORT "https://github.com/prusa3d/Slic3r/issues"
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "avrdude"
|
||||
#define PACKAGE_NAME "avrdude-slic3r"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "avrdude 6.3-20160220"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "avrdude"
|
||||
#define PACKAGE_TARNAME "avrdude-slic3r"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#define PACKAGE_URL ""
|
||||
#define PACKAGE_URL "https://github.com/prusa3d/Slic3r"
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "6.3-20160220"
|
||||
|
|
|
|||
|
|
@ -96,13 +96,20 @@ void AvrDude::priv::unset_handlers()
|
|||
|
||||
|
||||
int AvrDude::priv::run_one(const std::vector<std::string> &args) {
|
||||
std::vector<char*> c_args {{ const_cast<char*>(PACKAGE_NAME) }};
|
||||
std::vector<char*> c_args {{ const_cast<char*>(PACKAGE) }};
|
||||
std::string command_line { PACKAGE };
|
||||
|
||||
for (const auto &arg : args) {
|
||||
c_args.push_back(const_cast<char*>(arg.data()));
|
||||
command_line.push_back(' ');
|
||||
command_line.append(arg);
|
||||
}
|
||||
command_line.push_back('\n');
|
||||
|
||||
HandlerGuard guard(*this);
|
||||
|
||||
message_fn(command_line.c_str(), command_line.size());
|
||||
|
||||
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data(), sys_config.c_str());
|
||||
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -1082,6 +1082,7 @@ int avrdude_main(int argc, char * argv [], const char *sys_config)
|
|||
if (rc < 0) {
|
||||
exitrc = 1;
|
||||
pgm->ppidata = 0; /* clear all bits at exit */
|
||||
avrdude_message(MSG_INFO, "%s: Could not open port: %s\n", progname, port);
|
||||
goto main_exit;
|
||||
}
|
||||
is_open = 1;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,9 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
|
|||
if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) {
|
||||
group_attrib[i].is_solid = true;
|
||||
group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
|
||||
group_attrib[i].pattern = surface.is_external() ? layerm.region()->config().external_fill_pattern.value : ipRectilinear;
|
||||
group_attrib[i].pattern = surface.is_external() ?
|
||||
(surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) :
|
||||
ipRectilinear;
|
||||
}
|
||||
}
|
||||
// Loop through solid groups, find compatible groups and append them to this one.
|
||||
|
|
@ -161,7 +163,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
|
|||
if (surface.is_solid()) {
|
||||
density = 100.;
|
||||
fill_pattern = (surface.is_external() && ! is_bridge) ?
|
||||
layerm.region()->config().external_fill_pattern.value :
|
||||
(surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) :
|
||||
ipRectilinear;
|
||||
} else if (density <= 0)
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ namespace Slic3r {
|
|||
typedef std::map<int, ObjectMetadata> IdToMetadataMap;
|
||||
typedef std::map<int, Geometry> IdToGeometryMap;
|
||||
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
|
||||
typedef std::map<int, std::vector<Vec3f>> IdToSlaSupportPointsMap;
|
||||
typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
|
||||
|
||||
// Version of the 3mf file
|
||||
unsigned int m_version;
|
||||
|
|
@ -776,10 +776,19 @@ namespace Slic3r {
|
|||
std::vector<std::string> objects;
|
||||
boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
|
||||
|
||||
// Info on format versioning - see 3mf.hpp
|
||||
int version = 0;
|
||||
if (!objects.empty() && objects[0].find("support_points_format_version=") != std::string::npos) {
|
||||
objects[0].erase(objects[0].begin(), objects[0].begin() + 30); // removes the string
|
||||
version = std::stoi(objects[0]);
|
||||
objects.erase(objects.begin()); // pop the header
|
||||
}
|
||||
|
||||
for (const std::string& object : objects)
|
||||
{
|
||||
std::vector<std::string> object_data;
|
||||
boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
|
||||
|
||||
if (object_data.size() != 2)
|
||||
{
|
||||
add_error("Error while reading object data");
|
||||
|
|
@ -811,10 +820,24 @@ namespace Slic3r {
|
|||
std::vector<std::string> object_data_points;
|
||||
boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off);
|
||||
|
||||
std::vector<Vec3f> sla_support_points;
|
||||
std::vector<sla::SupportPoint> sla_support_points;
|
||||
|
||||
if (version == 0) {
|
||||
for (unsigned int i=0; i<object_data_points.size(); i+=3)
|
||||
sla_support_points.push_back(Vec3d(std::atof(object_data_points[i+0].c_str()), std::atof(object_data_points[i+1].c_str()), std::atof(object_data_points[i+2].c_str())).cast<float>());
|
||||
sla_support_points.emplace_back(std::atof(object_data_points[i+0].c_str()),
|
||||
std::atof(object_data_points[i+1].c_str()),
|
||||
std::atof(object_data_points[i+2].c_str()),
|
||||
0.4f,
|
||||
false);
|
||||
}
|
||||
if (version == 1) {
|
||||
for (unsigned int i=0; i<object_data_points.size(); i+=5)
|
||||
sla_support_points.emplace_back(std::atof(object_data_points[i+0].c_str()),
|
||||
std::atof(object_data_points[i+1].c_str()),
|
||||
std::atof(object_data_points[i+2].c_str()),
|
||||
std::atof(object_data_points[i+3].c_str()),
|
||||
std::atof(object_data_points[i+4].c_str()));
|
||||
}
|
||||
|
||||
if (!sla_support_points.empty())
|
||||
m_sla_support_points.insert(IdToSlaSupportPointsMap::value_type(object_id, sla_support_points));
|
||||
|
|
@ -1483,7 +1506,7 @@ namespace Slic3r {
|
|||
if (metadata.key == NAME_KEY)
|
||||
volume->name = metadata.value;
|
||||
else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
|
||||
volume->set_type(ModelVolume::PARAMETER_MODIFIER);
|
||||
volume->set_type(ModelVolumeType::PARAMETER_MODIFIER);
|
||||
else if (metadata.key == VOLUME_TYPE_KEY)
|
||||
volume->set_type(ModelVolume::type_from_string(metadata.value));
|
||||
else
|
||||
|
|
@ -1961,7 +1984,7 @@ namespace Slic3r {
|
|||
for (const ModelObject* object : model.objects)
|
||||
{
|
||||
++count;
|
||||
const std::vector<Vec3f>& sla_support_points = object->sla_support_points;
|
||||
const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
|
||||
if (!sla_support_points.empty())
|
||||
{
|
||||
sprintf(buffer, "object_id=%d|", count);
|
||||
|
|
@ -1970,7 +1993,7 @@ namespace Slic3r {
|
|||
// Store the layer height profile as a single space separated list.
|
||||
for (size_t i = 0; i < sla_support_points.size(); ++i)
|
||||
{
|
||||
sprintf(buffer, (i==0 ? "%f %f %f" : " %f %f %f"), sla_support_points[i](0), sla_support_points[i](1), sla_support_points[i](2));
|
||||
sprintf(buffer, (i==0 ? "%f %f %f %f %f" : " %f %f %f %f %f"), sla_support_points[i].pos(0), sla_support_points[i].pos(1), sla_support_points[i].pos(2), sla_support_points[i].head_front_radius, (float)sla_support_points[i].is_new_island);
|
||||
out += buffer;
|
||||
}
|
||||
out += "\n";
|
||||
|
|
@ -1979,6 +2002,9 @@ namespace Slic3r {
|
|||
|
||||
if (!out.empty())
|
||||
{
|
||||
// Adds version header at the beginning:
|
||||
out = std::string("support_points_format_version=") + std::to_string(support_points_format_version) + std::string("\n") + out;
|
||||
|
||||
if (!mz_zip_writer_add_mem(&archive, SLA_SUPPORT_POINTS_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
|
||||
{
|
||||
add_error("Unable to add sla support points file to archive");
|
||||
|
|
|
|||
|
|
@ -3,6 +3,23 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
/* The format for saving the SLA points was changing in the past. This enum holds the latest version that is being currently used.
|
||||
* Examples of the Slic3r_PE_sla_support_points.txt for historically used versions:
|
||||
|
||||
* version 0 : object_id=1|-12.055421 -2.658771 10.000000
|
||||
object_id=2|-14.051745 -3.570338 5.000000
|
||||
// no header and x,y,z positions of the points)
|
||||
|
||||
* version 1 : ThreeMF_support_points_version=1
|
||||
object_id=1|-12.055421 -2.658771 10.000000 0.4 0.0
|
||||
object_id=2|-14.051745 -3.570338 5.000000 0.6 1.0
|
||||
// introduced header with version number; x,y,z,head_size,is_new_island)
|
||||
*/
|
||||
|
||||
enum {
|
||||
support_points_format_version = 1
|
||||
};
|
||||
|
||||
class Model;
|
||||
class DynamicPrintConfig;
|
||||
|
||||
|
|
|
|||
|
|
@ -583,7 +583,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "sla_support_points") == 0) {
|
||||
// Parse object's layer height profile, a semicolon separated list of floats.
|
||||
unsigned char coord_idx = 0;
|
||||
Vec3f point(Vec3f::Zero());
|
||||
Eigen::Matrix<float, 5, 1, Eigen::DontAlign> point(Eigen::Matrix<float, 5, 1, Eigen::DontAlign>::Zero());
|
||||
char *p = const_cast<char*>(m_value[1].c_str());
|
||||
for (;;) {
|
||||
char *end = strchr(p, ';');
|
||||
|
|
@ -591,8 +591,8 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
*end = 0;
|
||||
|
||||
point(coord_idx) = atof(p);
|
||||
if (++coord_idx == 3) {
|
||||
m_object->sla_support_points.push_back(point);
|
||||
if (++coord_idx == 5) {
|
||||
m_object->sla_support_points.push_back(sla::SupportPoint(point));
|
||||
coord_idx = 0;
|
||||
}
|
||||
if (end == nullptr)
|
||||
|
|
@ -604,7 +604,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
if (strcmp(opt_key, "modifier") == 0) {
|
||||
// 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);
|
||||
m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART);
|
||||
} else if (strcmp(opt_key, "volume_type") == 0) {
|
||||
m_volume->set_type(ModelVolume::type_from_string(m_value[1]));
|
||||
}
|
||||
|
|
@ -900,14 +900,14 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
|||
}
|
||||
//FIXME Store the layer height ranges (ModelObject::layer_height_ranges)
|
||||
|
||||
const std::vector<Vec3f>& sla_support_points = object->sla_support_points;
|
||||
const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
|
||||
if (!sla_support_points.empty()) {
|
||||
// Store the SLA supports as a single semicolon separated list.
|
||||
stream << " <metadata type=\"slic3r.sla_support_points\">";
|
||||
for (size_t i = 0; i < sla_support_points.size(); ++i) {
|
||||
if (i != 0)
|
||||
stream << ";";
|
||||
stream << sla_support_points[i](0) << ";" << sla_support_points[i](1) << ";" << sla_support_points[i](2);
|
||||
stream << sla_support_points[i].pos(0) << ";" << sla_support_points[i].pos(1) << ";" << sla_support_points[i].pos(2) << ";" << sla_support_points[i].head_front_radius << ";" << sla_support_points[i].is_new_island;
|
||||
}
|
||||
stream << "\n </metadata>\n";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1480,32 +1480,32 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
|
|||
return m_convex_hull;
|
||||
}
|
||||
|
||||
ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
|
||||
ModelVolumeType ModelVolume::type_from_string(const std::string &s)
|
||||
{
|
||||
// Legacy support
|
||||
if (s == "1")
|
||||
return PARAMETER_MODIFIER;
|
||||
return ModelVolumeType::PARAMETER_MODIFIER;
|
||||
// New type (supporting the support enforcers & blockers)
|
||||
if (s == "ModelPart")
|
||||
return MODEL_PART;
|
||||
return ModelVolumeType::MODEL_PART;
|
||||
if (s == "ParameterModifier")
|
||||
return PARAMETER_MODIFIER;
|
||||
return ModelVolumeType::PARAMETER_MODIFIER;
|
||||
if (s == "SupportEnforcer")
|
||||
return SUPPORT_ENFORCER;
|
||||
return ModelVolumeType::SUPPORT_ENFORCER;
|
||||
if (s == "SupportBlocker")
|
||||
return SUPPORT_BLOCKER;
|
||||
return ModelVolumeType::SUPPORT_BLOCKER;
|
||||
assert(s == "0");
|
||||
// Default value if invalud type string received.
|
||||
return MODEL_PART;
|
||||
return ModelVolumeType::MODEL_PART;
|
||||
}
|
||||
|
||||
std::string ModelVolume::type_to_string(const Type t)
|
||||
std::string ModelVolume::type_to_string(const ModelVolumeType t)
|
||||
{
|
||||
switch (t) {
|
||||
case MODEL_PART: return "ModelPart";
|
||||
case PARAMETER_MODIFIER: return "ParameterModifier";
|
||||
case SUPPORT_ENFORCER: return "SupportEnforcer";
|
||||
case SUPPORT_BLOCKER: return "SupportBlocker";
|
||||
case ModelVolumeType::MODEL_PART: return "ModelPart";
|
||||
case ModelVolumeType::PARAMETER_MODIFIER: return "ParameterModifier";
|
||||
case ModelVolumeType::SUPPORT_ENFORCER: return "SupportEnforcer";
|
||||
case ModelVolumeType::SUPPORT_BLOCKER: return "SupportBlocker";
|
||||
default:
|
||||
assert(false);
|
||||
return "ModelPart";
|
||||
|
|
@ -1671,7 +1671,7 @@ bool model_object_list_extended(const Model &model_old, const Model &model_new)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type)
|
||||
bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolumeType type)
|
||||
{
|
||||
bool modifiers_differ = false;
|
||||
size_t i_old, i_new;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
#include "Geometry.hpp"
|
||||
#include <libslic3r/SLA/SLACommon.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -48,6 +49,8 @@ struct ModelID
|
|||
bool operator<=(const ModelID &rhs) const { return this->id <= rhs.id; }
|
||||
bool operator>=(const ModelID &rhs) const { return this->id >= rhs.id; }
|
||||
|
||||
bool valid() const { return id != 0; }
|
||||
|
||||
size_t id;
|
||||
};
|
||||
|
||||
|
|
@ -175,7 +178,8 @@ public:
|
|||
|
||||
// This vector holds position of selected support points for SLA. The data are
|
||||
// saved in mesh coordinates to allow using them for several instances.
|
||||
std::vector<Vec3f> sla_support_points;
|
||||
// The format is (x, y, z, point_size, supports_island)
|
||||
std::vector<sla::SupportPoint> sla_support_points;
|
||||
|
||||
/* This vector accumulates the total translation applied to the object by the
|
||||
center_around_origin() method. Callers might want to apply the same translation
|
||||
|
|
@ -291,6 +295,15 @@ private:
|
|||
mutable bool m_raw_mesh_bounding_box_valid;
|
||||
};
|
||||
|
||||
// Declared outside of ModelVolume, so it could be forward declared.
|
||||
enum class ModelVolumeType : int {
|
||||
INVALID = -1,
|
||||
MODEL_PART = 0,
|
||||
PARAMETER_MODIFIER,
|
||||
SUPPORT_ENFORCER,
|
||||
SUPPORT_BLOCKER,
|
||||
};
|
||||
|
||||
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
|
||||
// ModelVolume instances are owned by a ModelObject.
|
||||
class ModelVolume : public ModelBase
|
||||
|
|
@ -303,23 +316,15 @@ public:
|
|||
// overriding the global Slic3r settings and the ModelObject settings.
|
||||
DynamicPrintConfig config;
|
||||
|
||||
enum Type {
|
||||
MODEL_TYPE_INVALID = -1,
|
||||
MODEL_PART = 0,
|
||||
PARAMETER_MODIFIER,
|
||||
SUPPORT_ENFORCER,
|
||||
SUPPORT_BLOCKER,
|
||||
};
|
||||
|
||||
// A parent object owning this modifier volume.
|
||||
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; }
|
||||
bool is_support_modifier() const { return m_type == SUPPORT_BLOCKER || m_type == SUPPORT_ENFORCER; }
|
||||
ModelVolumeType type() const { return m_type; }
|
||||
void set_type(const ModelVolumeType t) { m_type = t; }
|
||||
bool is_model_part() const { return m_type == ModelVolumeType::MODEL_PART; }
|
||||
bool is_modifier() const { return m_type == ModelVolumeType::PARAMETER_MODIFIER; }
|
||||
bool is_support_enforcer() const { return m_type == ModelVolumeType::SUPPORT_ENFORCER; }
|
||||
bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; }
|
||||
bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; }
|
||||
t_model_material_id material_id() const { return m_material_id; }
|
||||
void set_material_id(t_model_material_id material_id);
|
||||
ModelMaterial* material() const;
|
||||
|
|
@ -353,8 +358,8 @@ public:
|
|||
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);
|
||||
static ModelVolumeType type_from_string(const std::string &s);
|
||||
static std::string type_to_string(const ModelVolumeType t);
|
||||
|
||||
const Geometry::Transformation& get_transformation() const { return m_transformation; }
|
||||
void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; }
|
||||
|
|
@ -399,7 +404,7 @@ private:
|
|||
// Parent object owning this ModelVolume.
|
||||
ModelObject* object;
|
||||
// Is it an object to be printed, or a modifier volume?
|
||||
Type m_type;
|
||||
ModelVolumeType m_type;
|
||||
t_model_material_id m_material_id;
|
||||
// The convex hull of this model's mesh.
|
||||
TriangleMesh m_convex_hull;
|
||||
|
|
@ -411,13 +416,13 @@ private:
|
|||
// 1 -> is splittable
|
||||
int m_is_splittable {-1};
|
||||
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object)
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(ModelVolumeType::MODEL_PART), object(object)
|
||||
{
|
||||
if (mesh.stl.stats.number_of_facets > 1)
|
||||
calculate_convex_hull();
|
||||
}
|
||||
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) {}
|
||||
mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(ModelVolumeType::MODEL_PART), object(object) {}
|
||||
|
||||
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
|
||||
ModelVolume(ModelObject *object, const ModelVolume &other) :
|
||||
|
|
@ -629,7 +634,7 @@ extern bool model_object_list_extended(const Model &model_old, const Model &mode
|
|||
|
||||
// Test whether the new ModelObject contains a different set of volumes (or sorted in a different order)
|
||||
// than the old ModelObject.
|
||||
extern bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type);
|
||||
extern bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolumeType type);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ typedef Point Vector;
|
|||
// Vector types with a fixed point coordinate base type.
|
||||
typedef Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign> Vec2crd;
|
||||
typedef Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign> Vec3crd;
|
||||
typedef Eigen::Matrix<int, 2, 1, Eigen::DontAlign> Vec2i;
|
||||
typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> Vec3i;
|
||||
typedef Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign> Vec2i64;
|
||||
typedef Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign> Vec3i64;
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ bool Print::is_step_done(PrintObjectStep step) const
|
|||
return false;
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
for (const PrintObject *object : m_objects)
|
||||
if (! object->m_state.is_done_unguarded(step))
|
||||
if (! object->is_step_done_unguarded(step))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -549,10 +549,14 @@ void Print::model_volume_list_update_supports(ModelObject &model_object_dst, con
|
|||
assert(! it->second); // not consumed yet
|
||||
it->second = true;
|
||||
ModelVolume *model_volume_dst = const_cast<ModelVolume*>(it->first);
|
||||
assert(model_volume_dst->type() == model_volume_src->type());
|
||||
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
|
||||
assert((model_volume_dst->is_support_modifier() && model_volume_src->is_support_modifier()) || model_volume_dst->type() == model_volume_src->type());
|
||||
model_object_dst.volumes.emplace_back(model_volume_dst);
|
||||
if (model_volume_dst->is_support_modifier())
|
||||
if (model_volume_dst->is_support_modifier()) {
|
||||
// For support modifiers, the type may have been switched from blocker to enforcer and vice versa.
|
||||
model_volume_dst->set_type(model_volume_src->type());
|
||||
model_volume_dst->set_transformation(model_volume_src->get_transformation());
|
||||
}
|
||||
assert(model_volume_dst->get_matrix().isApprox(model_volume_src->get_matrix()));
|
||||
} else {
|
||||
// The volume was not found in the old list. Create a new copy.
|
||||
|
|
@ -567,7 +571,7 @@ void Print::model_volume_list_update_supports(ModelObject &model_object_dst, con
|
|||
delete mv_with_status.first;
|
||||
}
|
||||
|
||||
static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolume::Type type)
|
||||
static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, const ModelObject &model_object_src, const ModelVolumeType type)
|
||||
{
|
||||
size_t i_src, i_dst;
|
||||
for (i_src = 0, i_dst = 0; i_src < model_object_src.volumes.size() && i_dst < model_object_dst.volumes.size();) {
|
||||
|
|
@ -713,7 +717,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
if (model.id() != m_model.id()) {
|
||||
// Kill everything, initialize from scratch.
|
||||
// Stop background processing.
|
||||
this->call_cancell_callback();
|
||||
this->call_cancel_callback();
|
||||
update_apply_status(this->invalidate_all_steps());
|
||||
for (PrintObject *object : m_objects) {
|
||||
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
|
||||
|
|
@ -745,7 +749,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
} else {
|
||||
// Reorder the objects, add new objects.
|
||||
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
|
||||
this->call_cancell_callback();
|
||||
this->call_cancel_callback();
|
||||
update_apply_status(this->invalidate_step(psGCodeExport));
|
||||
// Second create a new list of objects.
|
||||
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
|
||||
|
|
@ -837,10 +841,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
|
||||
const ModelObject &model_object_new = *model.objects[idx_model_object];
|
||||
// Check whether a model part volume was added or removed, their transformations or order changed.
|
||||
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::MODEL_PART);
|
||||
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::PARAMETER_MODIFIER);
|
||||
bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::SUPPORT_BLOCKER);
|
||||
bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::SUPPORT_ENFORCER);
|
||||
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
|
||||
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
|
||||
bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER);
|
||||
bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
|
||||
if (model_parts_differ || modifiers_differ ||
|
||||
model_object.origin_translation != model_object_new.origin_translation ||
|
||||
model_object.layer_height_ranges != model_object_new.layer_height_ranges ||
|
||||
|
|
@ -855,7 +859,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
model_object.assign_copy(model_object_new);
|
||||
} else if (support_blockers_differ || support_enforcers_differ) {
|
||||
// First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
|
||||
this->call_cancell_callback();
|
||||
this->call_cancel_callback();
|
||||
update_apply_status(false);
|
||||
// Invalidate just the supports step.
|
||||
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
||||
|
|
@ -882,8 +886,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
}
|
||||
// Synchronize (just copy) the remaining data of ModelVolumes (name, config).
|
||||
//FIXME What to do with m_material_id?
|
||||
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolume::MODEL_PART);
|
||||
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolume::PARAMETER_MODIFIER);
|
||||
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
|
||||
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
|
||||
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
|
||||
model_object.name = model_object_new.name;
|
||||
model_object.input_file = model_object_new.input_file;
|
||||
|
|
@ -956,7 +960,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
}
|
||||
}
|
||||
if (m_objects != print_objects_new) {
|
||||
this->call_cancell_callback();
|
||||
this->call_cancel_callback();
|
||||
update_apply_status(this->invalidate_all_steps());
|
||||
m_objects = print_objects_new;
|
||||
// Delete the PrintObjects marked as Unknown or Deleted.
|
||||
|
|
@ -1502,7 +1506,8 @@ void Print::export_gcode(const std::string &path_template, GCodePreviewData *pre
|
|||
// The following call may die if the output_filename_format template substitution fails.
|
||||
std::string path = this->output_filepath(path_template);
|
||||
std::string message = "Exporting G-code";
|
||||
if (! path.empty()) {
|
||||
if (! path.empty() && preview_data == nullptr) {
|
||||
// Only show the path if preview_data is not set -> running from command line.
|
||||
message += " to ";
|
||||
message += path;
|
||||
}
|
||||
|
|
@ -1863,7 +1868,7 @@ std::string Print::output_filename() const
|
|||
DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
|
||||
return this->PrintBase::output_filename(m_config.output_filename_format.value, "gcode", &config);
|
||||
}
|
||||
|
||||
/*
|
||||
// Shorten the dhms time by removing the seconds, rounding the dhm to full minutes
|
||||
// and removing spaces.
|
||||
static std::string short_time(const std::string &time)
|
||||
|
|
@ -1903,7 +1908,7 @@ static std::string short_time(const std::string &time)
|
|||
::sprintf(buffer, "%ds", seconds);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
*/
|
||||
DynamicConfig PrintStatistics::config() const
|
||||
{
|
||||
DynamicConfig config;
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ public:
|
|||
return state;
|
||||
}
|
||||
|
||||
bool is_started(StepType step, tbb::mutex &mtx) const {
|
||||
return this->state_with_timestamp(step, mtx).state == STARTED;
|
||||
}
|
||||
|
||||
bool is_done(StepType step, tbb::mutex &mtx) const {
|
||||
return this->state_with_timestamp(step, mtx).state == DONE;
|
||||
}
|
||||
|
|
@ -70,6 +74,10 @@ public:
|
|||
return m_state[step];
|
||||
}
|
||||
|
||||
bool is_started_unguarded(StepType step) const {
|
||||
return this->state_with_timestamp_unguarded(step).state == STARTED;
|
||||
}
|
||||
|
||||
bool is_done_unguarded(StepType step) const {
|
||||
return this->state_with_timestamp_unguarded(step).state == DONE;
|
||||
}
|
||||
|
|
@ -235,7 +243,24 @@ public:
|
|||
virtual ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) = 0;
|
||||
const Model& model() const { return m_model; }
|
||||
|
||||
struct TaskParams {
|
||||
TaskParams() : single_model_object(0), single_model_instance_only(false), to_object_step(-1), to_print_step(-1) {}
|
||||
// If non-empty, limit the processing to this ModelObject.
|
||||
ModelID single_model_object;
|
||||
// If set, only process single_model_object. Otherwise process everything, but single_model_object first.
|
||||
bool single_model_instance_only;
|
||||
// If non-negative, stop processing at the successive object step.
|
||||
int to_object_step;
|
||||
// If non-negative, stop processing at the successive print step.
|
||||
int to_print_step;
|
||||
};
|
||||
// After calling the apply() function, call set_task() to limit the task to be processed by process().
|
||||
virtual void set_task(const TaskParams ¶ms) {}
|
||||
// Perform the calculation. This is the only method that is to be called at a worker thread.
|
||||
virtual void process() = 0;
|
||||
// Clean up after process() finished, either with success, error or if canceled.
|
||||
// The adjustments on the Print / PrintObject data due to set_task() are to be reverted here.
|
||||
virtual void finalize() {}
|
||||
|
||||
struct SlicingStatus {
|
||||
SlicingStatus(int percent, const std::string &text, unsigned int flags = 0) : percent(percent), text(text), flags(flags) {}
|
||||
|
|
@ -245,7 +270,8 @@ public:
|
|||
enum FlagBits {
|
||||
DEFAULT,
|
||||
NO_RELOAD_SCENE = 0,
|
||||
RELOAD_SCENE = 1,
|
||||
RELOAD_SCENE = 1 << 1,
|
||||
RELOAD_SLA_SUPPORT_POINTS = 1 << 2,
|
||||
};
|
||||
// Bitmap of FlagBits
|
||||
unsigned int flags;
|
||||
|
|
@ -300,7 +326,7 @@ protected:
|
|||
|
||||
tbb::mutex& state_mutex() const { return m_state_mutex; }
|
||||
std::function<void()> cancel_callback() { return m_cancel_callback; }
|
||||
void call_cancell_callback() { m_cancel_callback(); }
|
||||
void call_cancel_callback() { m_cancel_callback(); }
|
||||
|
||||
// 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.
|
||||
|
|
@ -349,6 +375,9 @@ protected:
|
|||
bool invalidate_all_steps()
|
||||
{ return m_state.invalidate_all(this->cancel_callback()); }
|
||||
|
||||
bool is_step_started_unguarded(PrintStepEnum step) const { return m_state.is_started_unguarded(step); }
|
||||
bool is_step_done_unguarded(PrintStepEnum step) const { return m_state.is_done_unguarded(step); }
|
||||
|
||||
private:
|
||||
PrintState<PrintStepEnum, COUNT> m_state;
|
||||
};
|
||||
|
|
@ -382,6 +411,9 @@ protected:
|
|||
bool invalidate_all_steps()
|
||||
{ return m_state.invalidate_all(PrintObjectBase::cancel_callback(m_print)); }
|
||||
|
||||
bool is_step_started_unguarded(PrintObjectStepEnum step) const { return m_state.is_started_unguarded(step); }
|
||||
bool is_step_done_unguarded(PrintObjectStepEnum step) const { return m_state.is_done_unguarded(step); }
|
||||
|
||||
protected:
|
||||
// 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.
|
||||
|
|
|
|||
|
|
@ -362,12 +362,11 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("external_fill_pattern", coEnum);
|
||||
def->label = L("Top/bottom fill pattern");
|
||||
auto def_top_fill_pattern = def = this->add("top_fill_pattern", coEnum);
|
||||
def->label = L("Top fill pattern");
|
||||
def->category = L("Infill");
|
||||
def->tooltip = L("Fill pattern for top/bottom infill. This only affects the external visible layer, "
|
||||
"and not its adjacent solid shells.");
|
||||
def->cli = "external-fill-pattern|solid-fill-pattern=s";
|
||||
def->tooltip = L("Fill pattern for top infill. This only affects the top visible layer, and not its adjacent solid shells.");
|
||||
def->cli = "top-fill-pattern|external-fill-pattern|solid-fill-pattern=s";
|
||||
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
def->enum_values.push_back("rectilinear");
|
||||
def->enum_values.push_back("concentric");
|
||||
|
|
@ -379,8 +378,15 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back(L("Hilbert Curve"));
|
||||
def->enum_labels.push_back(L("Archimedean Chords"));
|
||||
def->enum_labels.push_back(L("Octagram Spiral"));
|
||||
// solid_fill_pattern is an obsolete equivalent to external_fill_pattern.
|
||||
def->aliases = { "solid_fill_pattern" };
|
||||
// solid_fill_pattern is an obsolete equivalent to top_fill_pattern/bottom_fill_pattern.
|
||||
def->aliases = { "solid_fill_pattern", "external_fill_pattern" };
|
||||
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
|
||||
|
||||
def = this->add("bottom_fill_pattern", coEnum);
|
||||
*def = *def_top_fill_pattern;
|
||||
def->label = L("Bottom Pattern");
|
||||
def->tooltip = L("Fill pattern for bottom infill. This only affects the bottom external visible layer, and not its adjacent solid shells.");
|
||||
def->cli = "bottom-fill-pattern|external-fill-pattern|solid-fill-pattern=s";
|
||||
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
|
||||
|
||||
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
|
||||
|
|
@ -2435,6 +2441,32 @@ void PrintConfigDef::init_sla_params()
|
|||
def->enum_labels.push_back(L("Portrait"));
|
||||
def->default_value = new ConfigOptionEnum<SLADisplayOrientation>(sladoPortrait);
|
||||
|
||||
def = this->add("fast_tilt_time", coFloat);
|
||||
def->label = L("Fast");
|
||||
def->full_label = L("Fast tilt");
|
||||
def->tooltip = L("Time of the fast tilt");
|
||||
def->sidetext = L("s");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionFloat(5.);
|
||||
|
||||
def = this->add("slow_tilt_time", coFloat);
|
||||
def->label = L("Slow");
|
||||
def->full_label = L("Slow tilt");
|
||||
def->tooltip = L("Time of the slow tilt");
|
||||
def->sidetext = L("s");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionFloat(8.);
|
||||
|
||||
def = this->add("area_fill", coFloat);
|
||||
def->label = L("Area fill");
|
||||
def->tooltip = L("The percentage of the bed area. \nIf the print area exceeds the specified value, \nthen a slow tilt will be used, otherwise - a fast tilt");
|
||||
def->sidetext = L("%");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionFloat(50.);
|
||||
|
||||
def = this->add("printer_correction", coFloats);
|
||||
def->full_label = L("Printer scaling correction");
|
||||
def->tooltip = L("Printer scaling correction");
|
||||
|
|
@ -2450,6 +2482,14 @@ void PrintConfigDef::init_sla_params()
|
|||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloat(0.3);
|
||||
|
||||
def = this->add("faded_layers", coInt);
|
||||
def->label = L("Faded layers");
|
||||
def->tooltip = L("Number of the layers needed for the exposure time fade from initial exposure time to the exposure time");
|
||||
def->min = 3;
|
||||
def->max = 20;
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionInt(10);
|
||||
|
||||
def = this->add("exposure_time", coFloat);
|
||||
def->label = L("Exposure time");
|
||||
def->tooltip = L("Exposure time");
|
||||
|
|
@ -2630,28 +2670,19 @@ void PrintConfigDef::init_sla_params()
|
|||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloat(5.0);
|
||||
|
||||
def = this->add("support_density_at_horizontal", coInt);
|
||||
def->label = L("Density on horizontal surfaces");
|
||||
def = this->add("support_points_density_relative", coInt);
|
||||
def->label = L("Support points density");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("How many support points (approximately) should be placed on horizontal surface.");
|
||||
def->sidetext = L("points per square dm");
|
||||
def->tooltip = L("This is a relative measure of support points density.");
|
||||
def->sidetext = L("%");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionInt(500);
|
||||
def->default_value = new ConfigOptionInt(100);
|
||||
|
||||
def = this->add("support_density_at_45", coInt);
|
||||
def->label = L("Density on surfaces at 45 degrees");
|
||||
def = this->add("support_points_minimal_distance", coFloat);
|
||||
def->label = L("Minimal distance of the support points");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("How many support points (approximately) should be placed on surface sloping at 45 degrees.");
|
||||
def->sidetext = L("points per square dm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionInt(250);
|
||||
|
||||
def = this->add("support_minimal_z", coFloat);
|
||||
def->label = L("Minimal support point height");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("No support points will be placed lower than this value from the bottom.");
|
||||
def->tooltip = L("No support points will be placed closer than this threshold.");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
|
|
@ -2699,6 +2730,17 @@ void PrintConfigDef::init_sla_params()
|
|||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloat(1.0);
|
||||
|
||||
def = this->add("pad_wall_tilt", coFloat);
|
||||
def->label = L("Pad wall tilt");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("The tilt of the pad wall relative to the bed plane. "
|
||||
"90 degrees means straight walls.");
|
||||
def->sidetext = L("degrees");
|
||||
def->cli = "";
|
||||
def->min = 0.1; // What should be the minimum?
|
||||
def->max = 90;
|
||||
def->default_value = new ConfigOptionFloat(45.0);
|
||||
}
|
||||
|
||||
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
|
||||
|
|
@ -2914,13 +2956,17 @@ std::string FullPrintConfig::validate()
|
|||
if (! print_config_def.get("fill_pattern")->has_enum_value(this->fill_pattern.serialize()))
|
||||
return "Invalid value for --fill-pattern";
|
||||
|
||||
// --external-fill-pattern
|
||||
if (! print_config_def.get("external_fill_pattern")->has_enum_value(this->external_fill_pattern.serialize()))
|
||||
return "Invalid value for --external-fill-pattern";
|
||||
// --top-fill-pattern
|
||||
if (! print_config_def.get("top_fill_pattern")->has_enum_value(this->top_fill_pattern.serialize()))
|
||||
return "Invalid value for --top-fill-pattern";
|
||||
|
||||
// --bottom-fill-pattern
|
||||
if (! print_config_def.get("bottom_fill_pattern")->has_enum_value(this->bottom_fill_pattern.serialize()))
|
||||
return "Invalid value for --bottom-fill-pattern";
|
||||
|
||||
// --fill-density
|
||||
if (fabs(this->fill_density.value - 100.) < EPSILON &&
|
||||
! print_config_def.get("external_fill_pattern")->has_enum_value(this->fill_pattern.serialize()))
|
||||
! print_config_def.get("top_fill_pattern")->has_enum_value(this->fill_pattern.serialize()))
|
||||
return "The selected fill pattern is not supposed to work at 100% density";
|
||||
|
||||
// --infill-every-layers
|
||||
|
|
|
|||
|
|
@ -462,7 +462,8 @@ public:
|
|||
ConfigOptionFloat bridge_flow_ratio;
|
||||
ConfigOptionFloat bridge_speed;
|
||||
ConfigOptionBool ensure_vertical_shell_thickness;
|
||||
ConfigOptionEnum<InfillPattern> external_fill_pattern;
|
||||
ConfigOptionEnum<InfillPattern> top_fill_pattern;
|
||||
ConfigOptionEnum<InfillPattern> bottom_fill_pattern;
|
||||
ConfigOptionFloatOrPercent external_perimeter_extrusion_width;
|
||||
ConfigOptionFloatOrPercent external_perimeter_speed;
|
||||
ConfigOptionBool external_perimeters_first;
|
||||
|
|
@ -504,7 +505,8 @@ protected:
|
|||
OPT_PTR(bridge_flow_ratio);
|
||||
OPT_PTR(bridge_speed);
|
||||
OPT_PTR(ensure_vertical_shell_thickness);
|
||||
OPT_PTR(external_fill_pattern);
|
||||
OPT_PTR(top_fill_pattern);
|
||||
OPT_PTR(bottom_fill_pattern);
|
||||
OPT_PTR(external_perimeter_extrusion_width);
|
||||
OPT_PTR(external_perimeter_speed);
|
||||
OPT_PTR(external_perimeters_first);
|
||||
|
|
@ -958,6 +960,9 @@ class SLAPrintObjectConfig : public StaticPrintConfig
|
|||
public:
|
||||
ConfigOptionFloat layer_height;
|
||||
|
||||
//Number of the layers needed for the exposure time fade [3;20]
|
||||
ConfigOptionInt faded_layers /*= 10*/;
|
||||
|
||||
// Enabling or disabling support creation
|
||||
ConfigOptionBool supports_enable;
|
||||
|
||||
|
|
@ -1002,9 +1007,8 @@ public:
|
|||
ConfigOptionFloat support_object_elevation /*= 5.0*/;
|
||||
|
||||
/////// Following options influence automatic support points placement:
|
||||
ConfigOptionInt support_density_at_horizontal;
|
||||
ConfigOptionInt support_density_at_45;
|
||||
ConfigOptionFloat support_minimal_z;
|
||||
ConfigOptionInt support_points_density_relative;
|
||||
ConfigOptionFloat support_points_minimal_distance;
|
||||
|
||||
// Now for the base pool (pad) /////////////////////////////////////////////
|
||||
|
||||
|
|
@ -1024,10 +1028,14 @@ public:
|
|||
// The smoothing radius of the pad edges
|
||||
ConfigOptionFloat pad_edge_radius /*= 1*/;
|
||||
|
||||
// The tilt of the pad wall...
|
||||
ConfigOptionFloat pad_wall_tilt;
|
||||
|
||||
protected:
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
OPT_PTR(layer_height);
|
||||
OPT_PTR(faded_layers);
|
||||
OPT_PTR(supports_enable);
|
||||
OPT_PTR(support_head_front_diameter);
|
||||
OPT_PTR(support_head_penetration);
|
||||
|
|
@ -1040,15 +1048,15 @@ protected:
|
|||
OPT_PTR(support_base_height);
|
||||
OPT_PTR(support_critical_angle);
|
||||
OPT_PTR(support_max_bridge_length);
|
||||
OPT_PTR(support_density_at_horizontal);
|
||||
OPT_PTR(support_density_at_45);
|
||||
OPT_PTR(support_minimal_z);
|
||||
OPT_PTR(support_points_density_relative);
|
||||
OPT_PTR(support_points_minimal_distance);
|
||||
OPT_PTR(support_object_elevation);
|
||||
OPT_PTR(pad_enable);
|
||||
OPT_PTR(pad_wall_thickness);
|
||||
OPT_PTR(pad_wall_height);
|
||||
OPT_PTR(pad_max_merge_distance);
|
||||
OPT_PTR(pad_edge_radius);
|
||||
OPT_PTR(pad_wall_tilt);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1085,6 +1093,9 @@ public:
|
|||
ConfigOptionInt display_pixels_y;
|
||||
ConfigOptionEnum<SLADisplayOrientation> display_orientation;
|
||||
ConfigOptionFloats printer_correction;
|
||||
ConfigOptionFloat fast_tilt_time;
|
||||
ConfigOptionFloat slow_tilt_time;
|
||||
ConfigOptionFloat area_fill;
|
||||
protected:
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
|
|
@ -1097,6 +1108,9 @@ protected:
|
|||
OPT_PTR(display_pixels_y);
|
||||
OPT_PTR(display_orientation);
|
||||
OPT_PTR(printer_correction);
|
||||
OPT_PTR(fast_tilt_time);
|
||||
OPT_PTR(slow_tilt_time);
|
||||
OPT_PTR(area_fill);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,17 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
// Used for addressing parameters of FilePrinter::set_statistics()
|
||||
enum ePrintStatistics
|
||||
{
|
||||
psUsedMaterial = 0,
|
||||
psNumFade,
|
||||
psNumSlow,
|
||||
psNumFast,
|
||||
|
||||
psCnt
|
||||
};
|
||||
|
||||
enum class FilePrinterFormat {
|
||||
SLA_PNGZIP,
|
||||
SVG
|
||||
|
|
@ -118,32 +129,39 @@ template<> class FilePrinter<FilePrinterFormat::SLA_PNGZIP>
|
|||
double m_layer_height = .0;
|
||||
Raster::Origin m_o = Raster::Origin::TOP_LEFT;
|
||||
|
||||
double m_used_material = 0.0;
|
||||
int m_cnt_fade_layers = 0;
|
||||
int m_cnt_slow_layers = 0;
|
||||
int m_cnt_fast_layers = 0;
|
||||
|
||||
std::string createIniContent(const std::string& projectname) {
|
||||
double layer_height = m_layer_height;
|
||||
// double layer_height = m_layer_height;
|
||||
|
||||
using std::string;
|
||||
using std::to_string;
|
||||
|
||||
auto expt_str = to_string(m_exp_time_s);
|
||||
auto expt_first_str = to_string(m_exp_time_first_s);
|
||||
auto stepnum_str = to_string(static_cast<unsigned>(800*layer_height));
|
||||
auto layerh_str = to_string(layer_height);
|
||||
// auto stepnum_str = to_string(static_cast<unsigned>(800*layer_height));
|
||||
auto layerh_str = to_string(m_layer_height);
|
||||
|
||||
const std::string cnt_fade_layers = to_string(m_cnt_fade_layers);
|
||||
const std::string cnt_slow_layers = to_string(m_cnt_slow_layers);
|
||||
const std::string cnt_fast_layers = to_string(m_cnt_fast_layers);
|
||||
const std::string used_material = to_string(m_used_material);
|
||||
|
||||
return string(
|
||||
"action = print\n"
|
||||
"jobDir = ") + projectname + "\n" +
|
||||
"expTime = " + expt_str + "\n"
|
||||
"expTimeFirst = " + expt_first_str + "\n"
|
||||
"stepNum = " + stepnum_str + "\n"
|
||||
"wifiOn = 1\n"
|
||||
"tiltSlow = 60\n"
|
||||
"tiltFast = 15\n"
|
||||
"numFade = 10\n"
|
||||
"startdelay = 0\n"
|
||||
"numFade = " + cnt_fade_layers + "\n"
|
||||
"layerHeight = " + layerh_str + "\n"
|
||||
"noteInfo = "
|
||||
"expTime="+expt_str+"+resinType=generic+layerHeight="
|
||||
+layerh_str+"+printer=DWARF3\n";
|
||||
"expTime = "+expt_str+" + resinType = generic+layerHeight = "
|
||||
+layerh_str+" + printer = DWARF3\n"
|
||||
"usedMaterial = " + used_material + "\n"
|
||||
"numSlow = " + cnt_slow_layers + "\n"
|
||||
"numFast = " + cnt_fast_layers + "\n";
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -277,6 +295,17 @@ public:
|
|||
out.close();
|
||||
m_layers_rst[i].first.reset();
|
||||
}
|
||||
|
||||
void set_statistics(const std::vector<double> statistics)
|
||||
{
|
||||
if (statistics.size() != psCnt)
|
||||
return;
|
||||
|
||||
m_used_material = statistics[psUsedMaterial];
|
||||
m_cnt_fade_layers = int(statistics[psNumFade]);
|
||||
m_cnt_slow_layers = int(statistics[psNumSlow]);
|
||||
m_cnt_fast_layers = int(statistics[psNumFast]);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -498,7 +498,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||
|| opt_key == "bridge_angle") {
|
||||
steps.emplace_back(posPrepareInfill);
|
||||
} else if (
|
||||
opt_key == "external_fill_pattern"
|
||||
opt_key == "top_fill_pattern"
|
||||
|| opt_key == "bottom_fill_pattern"
|
||||
|| opt_key == "external_fill_link_max_length"
|
||||
|| opt_key == "fill_angle"
|
||||
|| opt_key == "fill_pattern"
|
||||
|
|
|
|||
|
|
@ -1,47 +1,23 @@
|
|||
#include "igl/random_points_on_mesh.h"
|
||||
#include "igl/AABB.h"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
|
||||
#include "SLAAutoSupports.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "ExPolygon.hpp"
|
||||
#include "SVG.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Tesselate.hpp"
|
||||
#include "libslic3r.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector<ExPolygons>& slices, const std::vector<float>& heights,
|
||||
const Config& config, std::function<void(void)> throw_on_cancel)
|
||||
: m_config(config), m_V(emesh.V()), m_F(emesh.F()), m_throw_on_cancel(throw_on_cancel)
|
||||
{
|
||||
// FIXME: It might be safer to get rid of the rand() calls altogether, because it is probably
|
||||
// not always thread-safe and can be slow if it is.
|
||||
srand(time(NULL)); // rand() is used by igl::random_point_on_mesh
|
||||
|
||||
// Find all separate islands that will need support. The coord_t number denotes height
|
||||
// of a point just below the mesh (so that we can later project the point precisely
|
||||
// on the mesh by raycasting (done by igl) and not risking we will place the point inside).
|
||||
std::vector<std::pair<ExPolygon, coord_t>> islands = find_islands(slices, heights);
|
||||
|
||||
// Uniformly cover each of the islands with support points.
|
||||
for (const auto& island : islands) {
|
||||
std::vector<Vec3d> points = uniformly_cover(island);
|
||||
m_throw_on_cancel();
|
||||
project_upward_onto_mesh(points);
|
||||
m_output.insert(m_output.end(), points.begin(), points.end());
|
||||
m_throw_on_cancel();
|
||||
}
|
||||
|
||||
// We are done with the islands. Let's sprinkle the rest of the mesh.
|
||||
// The function appends to m_output.
|
||||
sprinkle_mesh(mesh);
|
||||
}
|
||||
|
||||
|
||||
float SLAAutoSupports::approximate_geodesic_distance(const Vec3d& p1, const Vec3d& p2, Vec3d& n1, Vec3d& n2)
|
||||
/*float SLAAutoSupports::approximate_geodesic_distance(const Vec3d& p1, const Vec3d& p2, Vec3d& n1, Vec3d& n2)
|
||||
{
|
||||
n1.normalize();
|
||||
n2.normalize();
|
||||
|
|
@ -59,115 +35,6 @@ float SLAAutoSupports::approximate_geodesic_distance(const Vec3d& p1, const Vec3
|
|||
}
|
||||
|
||||
|
||||
void SLAAutoSupports::sprinkle_mesh(const TriangleMesh& mesh)
|
||||
{
|
||||
std::vector<Vec3d> points;
|
||||
// Loads the ModelObject raw_mesh and transforms it by first instance's transformation matrix (disregarding translation).
|
||||
// Instances only differ in z-rotation, so it does not matter which of them will be used for the calculation.
|
||||
// The supports point will be calculated on this mesh (so scaling ang vertical direction is correctly accounted for).
|
||||
// Results will be inverse-transformed to raw_mesh coordinates.
|
||||
//TriangleMesh mesh = m_model_object.raw_mesh();
|
||||
//Transform3d transformation_matrix = m_model_object.instances[0]->get_matrix(true/*dont_translate*/);
|
||||
//mesh.transform(transformation_matrix);
|
||||
|
||||
// Check that the object is thick enough to produce any support points
|
||||
BoundingBoxf3 bb = mesh.bounding_box();
|
||||
if (bb.size()(2) < m_config.minimal_z)
|
||||
return;
|
||||
|
||||
// All points that we curretly have must be transformed too, so distance to them is correcly calculated.
|
||||
//for (Vec3f& point : m_model_object.sla_support_points)
|
||||
// point = transformation_matrix.cast<float>() * point;
|
||||
|
||||
|
||||
// In order to calculate distance to already placed points, we must keep know which facet the point lies on.
|
||||
std::vector<Vec3d> facets_normals;
|
||||
|
||||
// Only points belonging to islands were added so far - they all lie on horizontal surfaces:
|
||||
for (unsigned int i=0; i<m_output.size(); ++i)
|
||||
facets_normals.push_back(Vec3d(0,0,-1));
|
||||
|
||||
// The AABB hierarchy will be used to find normals of already placed points.
|
||||
// The points added automatically will just push_back the new normal on the fly.
|
||||
/*igl::AABB<Eigen::MatrixXf,3> aabb;
|
||||
aabb.init(V, F);
|
||||
for (unsigned int i=0; i<m_model_object.sla_support_points.size(); ++i) {
|
||||
int facet_idx = 0;
|
||||
Eigen::Matrix<float, 1, 3> dump;
|
||||
Eigen::MatrixXf query_point = m_model_object.sla_support_points[i];
|
||||
aabb.squared_distance(V, F, query_point, facet_idx, dump);
|
||||
Vec3f a1 = V.row(F(facet_idx,1)) - V.row(F(facet_idx,0));
|
||||
Vec3f a2 = V.row(F(facet_idx,2)) - V.row(F(facet_idx,0));
|
||||
Vec3f normal = a1.cross(a2);
|
||||
normal.normalize();
|
||||
facets_normals.push_back(normal);
|
||||
}*/
|
||||
|
||||
// New potential support point is randomly generated on the mesh and distance to all already placed points is calculated.
|
||||
// In case it is never smaller than certain limit (depends on the new point's facet normal), the point is accepted.
|
||||
// The process stops after certain number of points is refused in a row.
|
||||
Vec3d point;
|
||||
Vec3d normal;
|
||||
int added_points = 0;
|
||||
int refused_points = 0;
|
||||
const int refused_limit = 30;
|
||||
// Angle at which the density reaches zero:
|
||||
const float threshold_angle = std::min(M_PI_2, M_PI_4 * acos(0.f/m_config.density_at_horizontal) / acos(m_config.density_at_45/m_config.density_at_horizontal));
|
||||
|
||||
size_t cancel_test_cntr = 0;
|
||||
while (refused_points < refused_limit) {
|
||||
if (++ cancel_test_cntr == 500) {
|
||||
// Don't call the cancellation routine too often as the multi-core cache synchronization
|
||||
// may be pretty expensive.
|
||||
m_throw_on_cancel();
|
||||
cancel_test_cntr = 0;
|
||||
}
|
||||
// Place a random point on the mesh and calculate corresponding facet's normal:
|
||||
Eigen::VectorXi FI;
|
||||
Eigen::MatrixXd B;
|
||||
igl::random_points_on_mesh(1, m_V, m_F, B, FI);
|
||||
point = B(0,0)*m_V.row(m_F(FI(0),0)) +
|
||||
B(0,1)*m_V.row(m_F(FI(0),1)) +
|
||||
B(0,2)*m_V.row(m_F(FI(0),2));
|
||||
if (point(2) - bb.min(2) < m_config.minimal_z)
|
||||
continue;
|
||||
|
||||
Vec3d a1 = m_V.row(m_F(FI(0),1)) - m_V.row(m_F(FI(0),0));
|
||||
Vec3d a2 = m_V.row(m_F(FI(0),2)) - m_V.row(m_F(FI(0),0));
|
||||
normal = a1.cross(a2);
|
||||
normal.normalize();
|
||||
|
||||
// calculate angle between the normal and vertical:
|
||||
float angle = angle_from_normal(normal.cast<float>());
|
||||
if (angle > threshold_angle)
|
||||
continue;
|
||||
|
||||
const float limit = distance_limit(angle);
|
||||
bool add_it = true;
|
||||
for (unsigned int i=0; i<points.size(); ++i) {
|
||||
if (approximate_geodesic_distance(points[i], point, facets_normals[i], normal) < limit) {
|
||||
add_it = false;
|
||||
++refused_points;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (add_it) {
|
||||
points.push_back(point.cast<double>());
|
||||
facets_normals.push_back(normal);
|
||||
++added_points;
|
||||
refused_points = 0;
|
||||
}
|
||||
}
|
||||
|
||||
m_output.insert(m_output.end(), points.begin(), points.end());
|
||||
|
||||
// Now transform all support points to mesh coordinates:
|
||||
//for (Vec3f& point : m_model_object.sla_support_points)
|
||||
// point = transformation_matrix.inverse().cast<float>() * point;
|
||||
}
|
||||
|
||||
|
||||
|
||||
float SLAAutoSupports::get_required_density(float angle) const
|
||||
{
|
||||
// calculation would be density_0 * cos(angle). To provide one more degree of freedom, we will scale the angle
|
||||
|
|
@ -179,10 +46,470 @@ float SLAAutoSupports::get_required_density(float angle) const
|
|||
float SLAAutoSupports::distance_limit(float angle) const
|
||||
{
|
||||
return 1./(2.4*get_required_density(angle));
|
||||
}*/
|
||||
|
||||
SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector<ExPolygons>& slices, const std::vector<float>& heights,
|
||||
const Config& config, std::function<void(void)> throw_on_cancel)
|
||||
: m_config(config), m_emesh(emesh), m_throw_on_cancel(throw_on_cancel)
|
||||
{
|
||||
process(slices, heights);
|
||||
project_onto_mesh(m_output);
|
||||
}
|
||||
|
||||
void SLAAutoSupports::project_onto_mesh(std::vector<sla::SupportPoint>& points) const
|
||||
{
|
||||
// The function makes sure that all the points are really exactly placed on the mesh.
|
||||
igl::Hit hit_up{0, 0, 0.f, 0.f, 0.f};
|
||||
igl::Hit hit_down{0, 0, 0.f, 0.f, 0.f};
|
||||
|
||||
// Use a reasonable granularity to account for the worker thread synchronization cost.
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, points.size(), 64),
|
||||
[this, &points](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t point_id = range.begin(); point_id < range.end(); ++ point_id) {
|
||||
if ((point_id % 16) == 0)
|
||||
// Don't call the following function too often as it flushes CPU write caches due to synchronization primitves.
|
||||
m_throw_on_cancel();
|
||||
Vec3f& p = points[point_id].pos;
|
||||
// Project the point upward and downward and choose the closer intersection with the mesh.
|
||||
//bool up = igl::ray_mesh_intersect(p.cast<float>(), Vec3f(0., 0., 1.), m_V, m_F, hit_up);
|
||||
//bool down = igl::ray_mesh_intersect(p.cast<float>(), Vec3f(0., 0., -1.), m_V, m_F, hit_down);
|
||||
|
||||
sla::EigenMesh3D::hit_result hit_up = m_emesh.query_ray_hit(p.cast<double>(), Vec3d(0., 0., 1.));
|
||||
sla::EigenMesh3D::hit_result hit_down = m_emesh.query_ray_hit(p.cast<double>(), Vec3d(0., 0., -1.));
|
||||
|
||||
bool up = hit_up.face() != -1;
|
||||
bool down = hit_down.face() != -1;
|
||||
|
||||
if (!up && !down)
|
||||
continue;
|
||||
|
||||
sla::EigenMesh3D::hit_result& hit = (!down || (hit_up.distance() < hit_down.distance())) ? hit_up : hit_down;
|
||||
//int fid = hit.face();
|
||||
//Vec3f bc(1-hit.u-hit.v, hit.u, hit.v);
|
||||
//p = (bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2))).cast<float>();
|
||||
|
||||
p = p + (hit.distance() * hit.direction()).cast<float>();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static std::vector<SLAAutoSupports::MyLayer> make_layers(
|
||||
const std::vector<ExPolygons>& slices, const std::vector<float>& heights,
|
||||
std::function<void(void)> throw_on_cancel)
|
||||
{
|
||||
assert(slices.size() == heights.size());
|
||||
|
||||
// Allocate empty layers.
|
||||
std::vector<SLAAutoSupports::MyLayer> layers;
|
||||
layers.reserve(slices.size());
|
||||
for (size_t i = 0; i < slices.size(); ++ i)
|
||||
layers.emplace_back(i, heights[i]);
|
||||
|
||||
// FIXME: calculate actual pixel area from printer config:
|
||||
//const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option<ConfigOptionFloat>("display_width") / wxGetApp().preset_bundle->project_config.option<ConfigOptionInt>("display_pixels_x"), 2.f); //
|
||||
const float pixel_area = pow(0.047f, 2.f);
|
||||
|
||||
// Use a reasonable granularity to account for the worker thread synchronization cost.
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers.size(), 32),
|
||||
[&layers, &slices, &heights, pixel_area, throw_on_cancel](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
|
||||
if ((layer_id % 8) == 0)
|
||||
// Don't call the following function too often as it flushes CPU write caches due to synchronization primitves.
|
||||
throw_on_cancel();
|
||||
SLAAutoSupports::MyLayer &layer = layers[layer_id];
|
||||
const ExPolygons &islands = slices[layer_id];
|
||||
//FIXME WTF?
|
||||
const float height = (layer_id>2 ? heights[layer_id-3] : heights[0]-(heights[1]-heights[0]));
|
||||
layer.islands.reserve(islands.size());
|
||||
for (const ExPolygon &island : islands) {
|
||||
float area = float(island.area() * SCALING_FACTOR * SCALING_FACTOR);
|
||||
if (area >= pixel_area)
|
||||
//FIXME this is not a correct centroid of a polygon with holes.
|
||||
layer.islands.emplace_back(layer, island, get_extents(island.contour), Slic3r::unscale(island.contour.centroid()).cast<float>(), area, height);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Calculate overlap of successive layers. Link overlapping islands.
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(1, layers.size(), 8),
|
||||
[&layers, &heights, throw_on_cancel](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) {
|
||||
if ((layer_id % 2) == 0)
|
||||
// Don't call the following function too often as it flushes CPU write caches due to synchronization primitves.
|
||||
throw_on_cancel();
|
||||
SLAAutoSupports::MyLayer &layer_above = layers[layer_id];
|
||||
SLAAutoSupports::MyLayer &layer_below = layers[layer_id - 1];
|
||||
//FIXME WTF?
|
||||
const float height = (layer_id>2 ? heights[layer_id-3] : heights[0]-(heights[1]-heights[0]));
|
||||
const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]);
|
||||
const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports
|
||||
const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle)));
|
||||
//FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands.
|
||||
for (SLAAutoSupports::Structure &top : layer_above.islands) {
|
||||
for (SLAAutoSupports::Structure &bottom : layer_below.islands) {
|
||||
float overlap_area = top.overlap_area(bottom);
|
||||
if (overlap_area > 0) {
|
||||
top.islands_below.emplace_back(&bottom, overlap_area);
|
||||
bottom.islands_above.emplace_back(&top, overlap_area);
|
||||
}
|
||||
}
|
||||
if (! top.islands_below.empty()) {
|
||||
Polygons top_polygons = to_polygons(*top.polygon);
|
||||
Polygons bottom_polygons = top.polygons_below();
|
||||
top.overhangs = diff_ex(top_polygons, bottom_polygons);
|
||||
if (! top.overhangs.empty()) {
|
||||
top.overhangs_area = 0.f;
|
||||
std::vector<std::pair<ExPolygon*, float>> expolys_with_areas;
|
||||
for (ExPolygon &ex : top.overhangs) {
|
||||
float area = float(ex.area());
|
||||
expolys_with_areas.emplace_back(&ex, area);
|
||||
top.overhangs_area += area;
|
||||
}
|
||||
std::sort(expolys_with_areas.begin(), expolys_with_areas.end(),
|
||||
[](const std::pair<ExPolygon*, float> &p1, const std::pair<ExPolygon*, float> &p2)
|
||||
{ return p1.second > p2.second; });
|
||||
ExPolygons overhangs_sorted;
|
||||
for (auto &p : expolys_with_areas)
|
||||
overhangs_sorted.emplace_back(std::move(*p.first));
|
||||
top.overhangs = std::move(overhangs_sorted);
|
||||
top.overhangs_area *= float(SCALING_FACTOR * SCALING_FACTOR);
|
||||
top.dangling_areas = diff_ex(top_polygons, offset(bottom_polygons, between_layers_offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights)
|
||||
{
|
||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||
std::vector<std::pair<ExPolygon, coord_t>> islands;
|
||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
|
||||
|
||||
std::vector<SLAAutoSupports::MyLayer> layers = make_layers(slices, heights, m_throw_on_cancel);
|
||||
|
||||
PointGrid3D point_grid;
|
||||
point_grid.cell_size = Vec3f(10.f, 10.f, 10.f);
|
||||
|
||||
for (unsigned int layer_id = 0; layer_id < layers.size(); ++ layer_id) {
|
||||
SLAAutoSupports::MyLayer *layer_top = &layers[layer_id];
|
||||
SLAAutoSupports::MyLayer *layer_bottom = (layer_id > 0) ? &layers[layer_id - 1] : nullptr;
|
||||
std::vector<float> support_force_bottom;
|
||||
if (layer_bottom != nullptr) {
|
||||
support_force_bottom.assign(layer_bottom->islands.size(), 0.f);
|
||||
for (size_t i = 0; i < layer_bottom->islands.size(); ++ i)
|
||||
support_force_bottom[i] = layer_bottom->islands[i].supports_force_total();
|
||||
}
|
||||
for (Structure &top : layer_top->islands)
|
||||
for (Structure::Link &bottom_link : top.islands_below) {
|
||||
Structure &bottom = *bottom_link.island;
|
||||
float centroids_dist = (bottom.centroid - top.centroid).norm();
|
||||
// Penalization resulting from centroid offset:
|
||||
// bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area));
|
||||
float &support_force = support_force_bottom[&bottom - layer_bottom->islands.data()];
|
||||
//FIXME this condition does not reflect a bifurcation into a one large island and one tiny island well, it incorrectly resets the support force to zero.
|
||||
// One should rather work with the overlap area vs overhang area.
|
||||
// support_force *= std::min(1.f, 1.f - std::min(1.f, 0.1f * centroids_dist * centroids_dist / bottom.area));
|
||||
// Penalization resulting from increasing polygon area:
|
||||
support_force *= std::min(1.f, 20.f * bottom.area / top.area);
|
||||
}
|
||||
// Let's assign proper support force to each of them:
|
||||
if (layer_id > 0) {
|
||||
for (Structure &below : layer_bottom->islands) {
|
||||
float below_support_force = support_force_bottom[&below - layer_bottom->islands.data()];
|
||||
float above_overlap_area = 0.f;
|
||||
for (Structure::Link &above_link : below.islands_above)
|
||||
above_overlap_area += above_link.overlap_area;
|
||||
for (Structure::Link &above_link : below.islands_above)
|
||||
above_link.island->supports_force_inherited += below_support_force * above_link.overlap_area / above_overlap_area;
|
||||
}
|
||||
}
|
||||
// Now iterate over all polygons and append new points if needed.
|
||||
for (Structure &s : layer_top->islands) {
|
||||
// Penalization resulting from large diff from the last layer:
|
||||
// s.supports_force_inherited /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area);
|
||||
s.supports_force_inherited /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area);
|
||||
|
||||
float force_deficit = s.support_force_deficit(m_config.tear_pressure());
|
||||
if (s.islands_below.empty()) { // completely new island - needs support no doubt
|
||||
uniformly_cover({ *s.polygon }, s, point_grid, true);
|
||||
} else if (! s.dangling_areas.empty()) {
|
||||
// Let's see if there's anything that overlaps enough to need supports:
|
||||
// What we now have in polygons needs support, regardless of what the forces are, so we can add them.
|
||||
//FIXME is it an island point or not? Vojtech thinks it is.
|
||||
uniformly_cover(s.dangling_areas, s, point_grid);
|
||||
} else if (! s.overhangs.empty()) {
|
||||
//FIXME add the support force deficit as a parameter, only cover until the defficiency is covered.
|
||||
uniformly_cover(s.overhangs, s, point_grid);
|
||||
}
|
||||
}
|
||||
|
||||
m_throw_on_cancel();
|
||||
|
||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||
/*std::string layer_num_str = std::string((i<10 ? "0" : "")) + std::string((i<100 ? "0" : "")) + std::to_string(i);
|
||||
output_expolygons(expolys_top, "top" + layer_num_str + ".svg");
|
||||
output_expolygons(diff, "diff" + layer_num_str + ".svg");
|
||||
if (!islands.empty())
|
||||
output_expolygons(islands, "islands" + layer_num_str + ".svg");*/
|
||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Vec2f> sample_expolygon(const ExPolygon &expoly, float samples_per_mm2, std::mt19937 &rng)
|
||||
{
|
||||
// Triangulate the polygon with holes into triplets of 3D points.
|
||||
std::vector<Vec2f> triangles = Slic3r::triangulate_expolygon_2f(expoly);
|
||||
|
||||
std::vector<Vec2f> out;
|
||||
if (! triangles.empty())
|
||||
{
|
||||
// Calculate area of each triangle.
|
||||
std::vector<float> areas;
|
||||
areas.reserve(triangles.size() / 3);
|
||||
for (size_t i = 0; i < triangles.size(); ) {
|
||||
const Vec2f &a = triangles[i ++];
|
||||
const Vec2f v1 = triangles[i ++] - a;
|
||||
const Vec2f v2 = triangles[i ++] - a;
|
||||
areas.emplace_back(0.5f * std::abs(cross2(v1, v2)));
|
||||
if (i != 3)
|
||||
// Prefix sum of the areas.
|
||||
areas.back() += areas[areas.size() - 2];
|
||||
}
|
||||
|
||||
size_t num_samples = size_t(ceil(areas.back() * samples_per_mm2));
|
||||
std::uniform_real_distribution<> random_triangle(0., double(areas.back()));
|
||||
std::uniform_real_distribution<> random_float(0., 1.);
|
||||
for (size_t i = 0; i < num_samples; ++ i) {
|
||||
double r = random_triangle(rng);
|
||||
size_t idx_triangle = std::min<size_t>(std::upper_bound(areas.begin(), areas.end(), (float)r) - areas.begin(), areas.size() - 1) * 3;
|
||||
// Select a random point on the triangle.
|
||||
double u = float(sqrt(random_float(rng)));
|
||||
double v = float(random_float(rng));
|
||||
const Vec2f &a = triangles[idx_triangle ++];
|
||||
const Vec2f &b = triangles[idx_triangle++];
|
||||
const Vec2f &c = triangles[idx_triangle];
|
||||
const Vec2f x = a * (1.f - u) + b * (u * (1.f - v)) + c * (v * u);
|
||||
out.emplace_back(x);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<Vec2f> sample_expolygon_with_boundary(const ExPolygon &expoly, float samples_per_mm2, float samples_per_mm_boundary, std::mt19937 &rng)
|
||||
{
|
||||
std::vector<Vec2f> out = sample_expolygon(expoly, samples_per_mm2, rng);
|
||||
double point_stepping_scaled = scale_(1.f) / samples_per_mm_boundary;
|
||||
for (size_t i_contour = 0; i_contour <= expoly.holes.size(); ++ i_contour) {
|
||||
const Polygon &contour = (i_contour == 0) ? expoly.contour : expoly.holes[i_contour - 1];
|
||||
const Points pts = contour.equally_spaced_points(point_stepping_scaled);
|
||||
for (size_t i = 0; i < pts.size(); ++ i)
|
||||
out.emplace_back(unscale<float>(pts[i].x()), unscale<float>(pts[i].y()));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<Vec2f> sample_expolygon_with_boundary(const ExPolygons &expolys, float samples_per_mm2, float samples_per_mm_boundary, std::mt19937 &rng)
|
||||
{
|
||||
std::vector<Vec2f> out;
|
||||
for (const ExPolygon &expoly : expolys)
|
||||
append(out, sample_expolygon_with_boundary(expoly, samples_per_mm2, samples_per_mm_boundary, rng));
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename REFUSE_FUNCTION>
|
||||
static inline std::vector<Vec2f> poisson_disk_from_samples(const std::vector<Vec2f> &raw_samples, float radius, REFUSE_FUNCTION refuse_function)
|
||||
{
|
||||
Vec2f corner_min(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
|
||||
for (const Vec2f &pt : raw_samples) {
|
||||
corner_min.x() = std::min(corner_min.x(), pt.x());
|
||||
corner_min.y() = std::min(corner_min.y(), pt.y());
|
||||
}
|
||||
|
||||
// Assign the raw samples to grid cells, sort the grid cells lexicographically.
|
||||
struct RawSample {
|
||||
Vec2f coord;
|
||||
Vec2i cell_id;
|
||||
};
|
||||
std::vector<RawSample> raw_samples_sorted;
|
||||
RawSample sample;
|
||||
for (const Vec2f &pt : raw_samples) {
|
||||
sample.coord = pt;
|
||||
sample.cell_id = ((pt - corner_min) / radius).cast<int>();
|
||||
raw_samples_sorted.emplace_back(sample);
|
||||
}
|
||||
std::sort(raw_samples_sorted.begin(), raw_samples_sorted.end(), [](const RawSample &lhs, const RawSample &rhs)
|
||||
{ return lhs.cell_id.x() < rhs.cell_id.x() || (lhs.cell_id.x() == rhs.cell_id.x() && lhs.cell_id.y() < rhs.cell_id.y()); });
|
||||
|
||||
struct PoissonDiskGridEntry {
|
||||
// Resulting output sample points for this cell:
|
||||
enum {
|
||||
max_positions = 4
|
||||
};
|
||||
Vec2f poisson_samples[max_positions];
|
||||
int num_poisson_samples = 0;
|
||||
|
||||
// Index into raw_samples:
|
||||
int first_sample_idx;
|
||||
int sample_cnt;
|
||||
};
|
||||
|
||||
struct CellIDHash {
|
||||
std::size_t operator()(const Vec2i &cell_id) const {
|
||||
return std::hash<int>()(cell_id.x()) ^ std::hash<int>()(cell_id.y() * 593);
|
||||
}
|
||||
};
|
||||
|
||||
// Map from cell IDs to hash_data. Each hash_data points to the range in raw_samples corresponding to that cell.
|
||||
// (We could just store the samples in hash_data. This implementation is an artifact of the reference paper, which
|
||||
// is optimizing for GPU acceleration that we haven't implemented currently.)
|
||||
typedef std::unordered_map<Vec2i, PoissonDiskGridEntry, CellIDHash> Cells;
|
||||
Cells cells;
|
||||
{
|
||||
typename Cells::iterator last_cell_id_it;
|
||||
Vec2i last_cell_id(-1, -1);
|
||||
for (int i = 0; i < raw_samples_sorted.size(); ++ i) {
|
||||
const RawSample &sample = raw_samples_sorted[i];
|
||||
if (sample.cell_id == last_cell_id) {
|
||||
// This sample is in the same cell as the previous, so just increase the count. Cells are
|
||||
// always contiguous, since we've sorted raw_samples_sorted by cell ID.
|
||||
++ last_cell_id_it->second.sample_cnt;
|
||||
} else {
|
||||
// This is a new cell.
|
||||
PoissonDiskGridEntry data;
|
||||
data.first_sample_idx = i;
|
||||
data.sample_cnt = 1;
|
||||
auto result = cells.insert({sample.cell_id, data});
|
||||
last_cell_id = sample.cell_id;
|
||||
last_cell_id_it = result.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int max_trials = 5;
|
||||
const float radius_squared = radius * radius;
|
||||
for (int trial = 0; trial < max_trials; ++ trial) {
|
||||
// Create sample points for each entry in cells.
|
||||
for (auto &it : cells) {
|
||||
const Vec2i &cell_id = it.first;
|
||||
PoissonDiskGridEntry &cell_data = it.second;
|
||||
// This cell's raw sample points start at first_sample_idx. On trial 0, try the first one. On trial 1, try first_sample_idx + 1.
|
||||
int next_sample_idx = cell_data.first_sample_idx + trial;
|
||||
if (trial >= cell_data.sample_cnt)
|
||||
// There are no more points to try for this cell.
|
||||
continue;
|
||||
const RawSample &candidate = raw_samples_sorted[next_sample_idx];
|
||||
// See if this point conflicts with any other points in this cell, or with any points in
|
||||
// neighboring cells. Note that it's possible to have more than one point in the same cell.
|
||||
bool conflict = refuse_function(candidate.coord);
|
||||
for (int i = -1; i < 2 && ! conflict; ++ i) {
|
||||
for (int j = -1; j < 2; ++ j) {
|
||||
const auto &it_neighbor = cells.find(cell_id + Vec2i(i, j));
|
||||
if (it_neighbor != cells.end()) {
|
||||
const PoissonDiskGridEntry &neighbor = it_neighbor->second;
|
||||
for (int i_sample = 0; i_sample < neighbor.num_poisson_samples; ++ i_sample)
|
||||
if ((neighbor.poisson_samples[i_sample] - candidate.coord).squaredNorm() < radius_squared) {
|
||||
conflict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! conflict) {
|
||||
// Store the new sample.
|
||||
assert(cell_data.num_poisson_samples < cell_data.max_positions);
|
||||
if (cell_data.num_poisson_samples < cell_data.max_positions)
|
||||
cell_data.poisson_samples[cell_data.num_poisson_samples ++] = candidate.coord;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the results to the output.
|
||||
std::vector<Vec2f> out;
|
||||
for (const auto& it : cells)
|
||||
for (int i = 0; i < it.second.num_poisson_samples; ++ i)
|
||||
out.emplace_back(it.second.poisson_samples[i]);
|
||||
return out;
|
||||
}
|
||||
|
||||
void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, bool is_new_island, bool just_one)
|
||||
{
|
||||
//int num_of_points = std::max(1, (int)((island.area()*pow(SCALING_FACTOR, 2) * m_config.tear_pressure)/m_config.support_force));
|
||||
|
||||
const float support_force_deficit = structure.support_force_deficit(m_config.tear_pressure());
|
||||
if (support_force_deficit < 0)
|
||||
return;
|
||||
|
||||
// Number of newly added points.
|
||||
const size_t poisson_samples_target = size_t(ceil(support_force_deficit / m_config.support_force()));
|
||||
|
||||
const float density_horizontal = m_config.tear_pressure() / m_config.support_force();
|
||||
//FIXME why?
|
||||
float poisson_radius = std::max(m_config.minimal_distance, 1.f / (5.f * density_horizontal));
|
||||
// const float poisson_radius = 1.f / (15.f * density_horizontal);
|
||||
const float samples_per_mm2 = 30.f / (float(M_PI) * poisson_radius * poisson_radius);
|
||||
// Minimum distance between samples, in 3D space.
|
||||
// float min_spacing = poisson_radius / 3.f;
|
||||
float min_spacing = poisson_radius;
|
||||
|
||||
//FIXME share the random generator. The random generator may be not so cheap to initialize, also we don't want the random generator to be restarted for each polygon.
|
||||
std::random_device rd;
|
||||
std::mt19937 rng(rd());
|
||||
std::vector<Vec2f> raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, rng);
|
||||
std::vector<Vec2f> poisson_samples;
|
||||
for (size_t iter = 0; iter < 4; ++ iter) {
|
||||
poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius,
|
||||
[&structure, &grid3d, min_spacing](const Vec2f &pos) {
|
||||
return grid3d.collides_with(pos, &structure, min_spacing);
|
||||
});
|
||||
if (poisson_samples.size() >= poisson_samples_target || m_config.minimal_distance > poisson_radius-EPSILON)
|
||||
break;
|
||||
float coeff = 0.5f;
|
||||
if (poisson_samples.size() * 2 > poisson_samples_target)
|
||||
coeff = float(poisson_samples.size()) / float(poisson_samples_target);
|
||||
poisson_radius = std::max(m_config.minimal_distance, poisson_radius * coeff);
|
||||
min_spacing = std::max(m_config.minimal_distance, min_spacing * coeff);
|
||||
}
|
||||
|
||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||
{
|
||||
static int irun = 0;
|
||||
Slic3r::SVG svg(debug_out_path("SLA_supports-uniformly_cover-%d.svg", irun ++), get_extents(islands));
|
||||
for (const ExPolygon &island : islands)
|
||||
svg.draw(island);
|
||||
for (const Vec2f &pt : raw_samples)
|
||||
svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "red");
|
||||
for (const Vec2f &pt : poisson_samples)
|
||||
svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "blue");
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
|
||||
// assert(! poisson_samples.empty());
|
||||
if (poisson_samples_target < poisson_samples.size()) {
|
||||
std::shuffle(poisson_samples.begin(), poisson_samples.end(), rng);
|
||||
poisson_samples.erase(poisson_samples.begin() + poisson_samples_target, poisson_samples.end());
|
||||
}
|
||||
for (const Vec2f &pt : poisson_samples) {
|
||||
m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, 0.2f, is_new_island);
|
||||
structure.supports_force_this_layer += m_config.support_force();
|
||||
grid3d.insert(pt, &structure);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||
void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, std::string filename) const
|
||||
void SLAAutoSupports::output_structures(const std::vector<Structure>& structures)
|
||||
{
|
||||
for (unsigned int i=0 ; i<structures.size(); ++i) {
|
||||
std::stringstream ss;
|
||||
ss << structures[i].unique_id.count() << "_" << std::setw(10) << std::setfill('0') << 1000 + (int)structures[i].height/1000 << ".png";
|
||||
output_expolygons(std::vector<ExPolygon>{*structures[i].polygon}, ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, const std::string &filename)
|
||||
{
|
||||
BoundingBox bb(Point(-30000000, -30000000), Point(30000000, 30000000));
|
||||
Slic3r::SVG svg_cummulative(filename, bb);
|
||||
|
|
@ -198,138 +525,6 @@ void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, std::string f
|
|||
svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05));
|
||||
}
|
||||
}
|
||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
|
||||
|
||||
std::vector<std::pair<ExPolygon, coord_t>> SLAAutoSupports::find_islands(const std::vector<ExPolygons>& slices, const std::vector<float>& heights) const
|
||||
{
|
||||
std::vector<std::pair<ExPolygon, coord_t>> islands;
|
||||
|
||||
struct PointAccessor {
|
||||
const Point* operator()(const Point &pt) const { return &pt; }
|
||||
};
|
||||
typedef ClosestPointInRadiusLookup<Point, PointAccessor> ClosestPointLookupType;
|
||||
|
||||
for (unsigned int i = 0; i<slices.size(); ++i) {
|
||||
const ExPolygons& expolys_top = slices[i];
|
||||
const ExPolygons& expolys_bottom = (i == 0 ? ExPolygons() : slices[i-1]);
|
||||
|
||||
std::string layer_num_str = std::string((i<10 ? "0" : "")) + std::string((i<100 ? "0" : "")) + std::to_string(i);
|
||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||
output_expolygons(expolys_top, "top" + layer_num_str + ".svg");
|
||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
|
||||
ExPolygons diff = diff_ex(expolys_top, expolys_bottom);
|
||||
|
||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||
output_expolygons(diff, "diff" + layer_num_str + ".svg");
|
||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
|
||||
|
||||
ClosestPointLookupType cpl(SCALED_EPSILON);
|
||||
for (const ExPolygon& expol : expolys_top) {
|
||||
for (const Point& p : expol.contour.points)
|
||||
cpl.insert(p);
|
||||
for (const Polygon& hole : expol.holes)
|
||||
for (const Point& p : hole.points)
|
||||
cpl.insert(p);
|
||||
// the lookup structure now contains all points from the top slice
|
||||
}
|
||||
|
||||
for (const ExPolygon& polygon : diff) {
|
||||
// we want to check all boundary points of the diff polygon
|
||||
bool island = true;
|
||||
for (const Point& p : polygon.contour.points) {
|
||||
if (cpl.find(p).second != 0) { // the point belongs to the bottom slice - this cannot be an island
|
||||
island = false;
|
||||
goto NO_ISLAND;
|
||||
}
|
||||
}
|
||||
for (const Polygon& hole : polygon.holes)
|
||||
for (const Point& p : hole.points)
|
||||
if (cpl.find(p).second != 0) {
|
||||
island = false;
|
||||
goto NO_ISLAND;
|
||||
}
|
||||
|
||||
if (island) { // all points of the diff polygon are from the top slice
|
||||
islands.push_back(std::make_pair(polygon, scale_(i!=0 ? heights[i-1] : heights[0]-(heights[1]-heights[0]))));
|
||||
}
|
||||
NO_ISLAND: ;// continue with next ExPolygon
|
||||
}
|
||||
|
||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||
//if (!islands.empty())
|
||||
// output_expolygons(islands, "islands" + layer_num_str + ".svg");
|
||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
|
||||
m_throw_on_cancel();
|
||||
}
|
||||
|
||||
return islands;
|
||||
}
|
||||
|
||||
std::vector<Vec3d> SLAAutoSupports::uniformly_cover(const std::pair<ExPolygon, coord_t>& island)
|
||||
{
|
||||
int num_of_points = std::max(1, (int)(island.first.area()*pow(SCALING_FACTOR, 2) * get_required_density(0)));
|
||||
|
||||
// In case there is just one point to place, we'll place it into the polygon's centroid (unless it lies in a hole).
|
||||
if (num_of_points == 1) {
|
||||
Point out(island.first.contour.centroid());
|
||||
|
||||
for (const auto& hole : island.first.holes)
|
||||
if (hole.contains(out))
|
||||
goto HOLE_HIT;
|
||||
return std::vector<Vec3d>{unscale(out(0), out(1), island.second)};
|
||||
}
|
||||
|
||||
HOLE_HIT:
|
||||
// In this case either the centroid lies in a hole, or there are multiple points
|
||||
// to place. We will cover the island another way.
|
||||
// For now we'll just place the points randomly not too close to the others.
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
std::uniform_real_distribution<> dis(0., 1.);
|
||||
|
||||
std::vector<Vec3d> island_new_points;
|
||||
const BoundingBox& bb = get_extents(island.first);
|
||||
const int refused_limit = 30;
|
||||
int refused_points = 0;
|
||||
while (refused_points < refused_limit) {
|
||||
Point out(bb.min(0) + bb.size()(0) * dis(gen),
|
||||
bb.min(1) + bb.size()(1) * dis(gen)) ;
|
||||
Vec3d unscaled_out = unscale(out(0), out(1), island.second);
|
||||
bool add_it = true;
|
||||
|
||||
if (!island.first.contour.contains(out))
|
||||
add_it = false;
|
||||
else
|
||||
for (const Polygon& hole : island.first.holes)
|
||||
if (hole.contains(out))
|
||||
add_it = false;
|
||||
|
||||
if (add_it) {
|
||||
for (const Vec3d& p : island_new_points) {
|
||||
if ((p - unscaled_out).squaredNorm() < distance_limit(0)) {
|
||||
add_it = false;
|
||||
++refused_points;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (add_it)
|
||||
island_new_points.emplace_back(unscaled_out);
|
||||
}
|
||||
return island_new_points;
|
||||
}
|
||||
|
||||
void SLAAutoSupports::project_upward_onto_mesh(std::vector<Vec3d>& points) const
|
||||
{
|
||||
Vec3f dir(0., 0., 1.);
|
||||
igl::Hit hit{0, 0, 0.f, 0.f, 0.f};
|
||||
for (Vec3d& p : points) {
|
||||
igl::ray_mesh_intersect(p.cast<float>(), dir, m_V, m_F, hit);
|
||||
int fid = hit.id;
|
||||
Vec3f bc(1-hit.u-hit.v, hit.u, hit.v);
|
||||
p = (bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2))).cast<double>();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
#ifndef SLAAUTOSUPPORTS_HPP_
|
||||
#define SLAAUTOSUPPORTS_HPP_
|
||||
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
#include <libslic3r/Point.hpp>
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
#include <libslic3r/SLA/SLASupportTree.hpp>
|
||||
#include <libslic3r/SLA/SLACommon.hpp>
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
// #define SLA_AUTOSUPPORTS_DEBUG
|
||||
|
||||
|
|
@ -12,36 +15,184 @@ namespace Slic3r {
|
|||
class SLAAutoSupports {
|
||||
public:
|
||||
struct Config {
|
||||
float density_at_horizontal;
|
||||
float density_at_45;
|
||||
float minimal_z;
|
||||
float density_relative;
|
||||
float minimal_distance;
|
||||
///////////////
|
||||
inline float support_force() const { return 10.f / density_relative; } // a force one point can support (arbitrary force unit)
|
||||
inline float tear_pressure() const { return 1.f; } // pressure that the display exerts (the force unit per mm2)
|
||||
};
|
||||
|
||||
SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector<ExPolygons>& slices,
|
||||
const std::vector<float>& heights, const Config& config, std::function<void(void)> throw_on_cancel);
|
||||
const std::vector<Vec3d>& output() { return m_output; }
|
||||
const std::vector<sla::SupportPoint>& output() { return m_output; }
|
||||
|
||||
private:
|
||||
std::vector<Vec3d> m_output;
|
||||
std::vector<Vec3d> m_normals;
|
||||
TriangleMesh mesh;
|
||||
static float angle_from_normal(const stl_normal& normal) { return acos((-normal.normalized())(2)); }
|
||||
float get_required_density(float angle) const;
|
||||
float distance_limit(float angle) const;
|
||||
static float approximate_geodesic_distance(const Vec3d& p1, const Vec3d& p2, Vec3d& n1, Vec3d& n2);
|
||||
std::vector<std::pair<ExPolygon, coord_t>> find_islands(const std::vector<ExPolygons>& slices, const std::vector<float>& heights) const;
|
||||
void sprinkle_mesh(const TriangleMesh& mesh);
|
||||
std::vector<Vec3d> uniformly_cover(const std::pair<ExPolygon, coord_t>& island);
|
||||
void project_upward_onto_mesh(std::vector<Vec3d>& points) const;
|
||||
struct MyLayer;
|
||||
|
||||
struct Structure {
|
||||
Structure(MyLayer &layer, const ExPolygon& poly, const BoundingBox &bbox, const Vec2f ¢roid, float area, float h) :
|
||||
layer(&layer), polygon(&poly), bbox(bbox), centroid(centroid), area(area), height(h)
|
||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||
void output_expolygons(const ExPolygons& expolys, std::string filename) const;
|
||||
, unique_id(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()))
|
||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
|
||||
{}
|
||||
MyLayer *layer;
|
||||
const ExPolygon* polygon = nullptr;
|
||||
const BoundingBox bbox;
|
||||
const Vec2f centroid = Vec2f::Zero();
|
||||
const float area = 0.f;
|
||||
float height = 0;
|
||||
// How well is this ExPolygon held to the print base?
|
||||
// Positive number, the higher the better.
|
||||
float supports_force_this_layer = 0.f;
|
||||
float supports_force_inherited = 0.f;
|
||||
float supports_force_total() const { return this->supports_force_this_layer + this->supports_force_inherited; }
|
||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||
std::chrono::milliseconds unique_id;
|
||||
#endif /* SLA_AUTOSUPPORTS_DEBUG */
|
||||
|
||||
struct Link {
|
||||
Link(Structure *island, float overlap_area) : island(island), overlap_area(overlap_area) {}
|
||||
Structure *island;
|
||||
float overlap_area;
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
// In release mode, use the optimized container.
|
||||
boost::container::small_vector<Link, 4> islands_above;
|
||||
boost::container::small_vector<Link, 4> islands_below;
|
||||
#else
|
||||
// In debug mode, use the standard vector, which is well handled by debugger visualizer.
|
||||
std::vector<Link> islands_above;
|
||||
std::vector<Link> islands_below;
|
||||
#endif
|
||||
ExPolygons dangling_areas;
|
||||
ExPolygons overhangs;
|
||||
float overhangs_area;
|
||||
|
||||
bool overlaps(const Structure &rhs) const {
|
||||
return this->bbox.overlap(rhs.bbox) && (this->polygon->overlaps(*rhs.polygon) || rhs.polygon->overlaps(*this->polygon));
|
||||
}
|
||||
float overlap_area(const Structure &rhs) const {
|
||||
double out = 0.;
|
||||
if (this->bbox.overlap(rhs.bbox)) {
|
||||
Polygons polys = intersection(to_polygons(*this->polygon), to_polygons(*rhs.polygon), false);
|
||||
for (const Polygon &poly : polys)
|
||||
out += poly.area();
|
||||
}
|
||||
return float(out);
|
||||
}
|
||||
float area_below() const {
|
||||
float area = 0.f;
|
||||
for (const Link &below : this->islands_below)
|
||||
area += below.island->area;
|
||||
return area;
|
||||
}
|
||||
Polygons polygons_below() const {
|
||||
size_t cnt = 0;
|
||||
for (const Link &below : this->islands_below)
|
||||
cnt += 1 + below.island->polygon->holes.size();
|
||||
Polygons out;
|
||||
out.reserve(cnt);
|
||||
for (const Link &below : this->islands_below) {
|
||||
out.emplace_back(below.island->polygon->contour);
|
||||
append(out, below.island->polygon->holes);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
ExPolygons expolygons_below() const {
|
||||
ExPolygons out;
|
||||
out.reserve(this->islands_below.size());
|
||||
for (const Link &below : this->islands_below)
|
||||
out.emplace_back(*below.island->polygon);
|
||||
return out;
|
||||
}
|
||||
// Positive deficit of the supports. If negative, this area is well supported. If positive, more supports need to be added.
|
||||
float support_force_deficit(const float tear_pressure) const { return this->area * tear_pressure - this->supports_force_total(); }
|
||||
};
|
||||
|
||||
struct MyLayer {
|
||||
MyLayer(const size_t layer_id, coordf_t print_z) : layer_id(layer_id), print_z(print_z) {}
|
||||
size_t layer_id;
|
||||
coordf_t print_z;
|
||||
std::vector<Structure> islands;
|
||||
};
|
||||
|
||||
struct RichSupportPoint {
|
||||
Vec3f position;
|
||||
Structure *island;
|
||||
};
|
||||
|
||||
struct PointGrid3D {
|
||||
struct GridHash {
|
||||
std::size_t operator()(const Vec3i &cell_id) const {
|
||||
return std::hash<int>()(cell_id.x()) ^ std::hash<int>()(cell_id.y() * 593) ^ std::hash<int>()(cell_id.z() * 7919);
|
||||
}
|
||||
};
|
||||
typedef std::unordered_multimap<Vec3i, RichSupportPoint, GridHash> Grid;
|
||||
|
||||
Vec3f cell_size;
|
||||
Grid grid;
|
||||
|
||||
Vec3i cell_id(const Vec3f &pos) {
|
||||
return Vec3i(int(floor(pos.x() / cell_size.x())),
|
||||
int(floor(pos.y() / cell_size.y())),
|
||||
int(floor(pos.z() / cell_size.z())));
|
||||
}
|
||||
|
||||
void insert(const Vec2f &pos, Structure *island) {
|
||||
RichSupportPoint pt;
|
||||
pt.position = Vec3f(pos.x(), pos.y(), float(island->layer->print_z));
|
||||
pt.island = island;
|
||||
grid.emplace(cell_id(pt.position), pt);
|
||||
}
|
||||
|
||||
bool collides_with(const Vec2f &pos, Structure *island, float radius) {
|
||||
Vec3f pos3d(pos.x(), pos.y(), float(island->layer->print_z));
|
||||
Vec3i cell = cell_id(pos3d);
|
||||
std::pair<Grid::const_iterator, Grid::const_iterator> it_pair = grid.equal_range(cell);
|
||||
if (collides_with(pos3d, radius, it_pair.first, it_pair.second))
|
||||
return true;
|
||||
for (int i = -1; i < 2; ++ i)
|
||||
for (int j = -1; j < 2; ++ j)
|
||||
for (int k = -1; k < 1; ++ k) {
|
||||
if (i == 0 && j == 0 && k == 0)
|
||||
continue;
|
||||
it_pair = grid.equal_range(cell + Vec3i(i, j, k));
|
||||
if (collides_with(pos3d, radius, it_pair.first, it_pair.second))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool collides_with(const Vec3f &pos, float radius, Grid::const_iterator it_begin, Grid::const_iterator it_end) {
|
||||
for (Grid::const_iterator it = it_begin; it != it_end; ++ it) {
|
||||
float dist2 = (it->second.position - pos).squaredNorm();
|
||||
if (dist2 < radius * radius)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
std::vector<sla::SupportPoint> m_output;
|
||||
|
||||
SLAAutoSupports::Config m_config;
|
||||
|
||||
float m_supports_force_total = 0.f;
|
||||
|
||||
void process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights);
|
||||
void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false);
|
||||
void project_onto_mesh(std::vector<sla::SupportPoint>& points) const;
|
||||
|
||||
#ifdef SLA_AUTOSUPPORTS_DEBUG
|
||||
static void output_expolygons(const ExPolygons& expolys, const std::string &filename);
|
||||
static void output_structures(const std::vector<Structure> &structures);
|
||||
#endif // SLA_AUTOSUPPORTS_DEBUG
|
||||
|
||||
std::function<void(void)> m_throw_on_cancel;
|
||||
const Eigen::MatrixXd& m_V;
|
||||
const Eigen::MatrixXi& m_F;
|
||||
const sla::EigenMesh3D& m_emesh;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,78 +4,177 @@
|
|||
#include "boost/log/trivial.hpp"
|
||||
#include "SLABoostAdapter.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Tesselate.hpp"
|
||||
|
||||
// For debugging:
|
||||
//#include <fstream>
|
||||
//#include <libnest2d/tools/benchmark.h>
|
||||
//#include "SVG.hpp"
|
||||
//#include "benchmark.h"
|
||||
|
||||
namespace Slic3r { namespace sla {
|
||||
|
||||
/// Convert the triangulation output to an intermediate mesh.
|
||||
Contour3D convert(const Polygons& triangles, coord_t z, bool dir) {
|
||||
|
||||
Pointf3s points;
|
||||
points.reserve(3*triangles.size());
|
||||
Indices indices;
|
||||
indices.reserve(points.size());
|
||||
|
||||
for(auto& tr : triangles) {
|
||||
auto c = coord_t(points.size()), b = c++, a = c++;
|
||||
if(dir) indices.emplace_back(a, b, c);
|
||||
else indices.emplace_back(c, b, a);
|
||||
for(auto& p : tr.points) {
|
||||
points.emplace_back(unscale(x(p), y(p), z));
|
||||
}
|
||||
}
|
||||
|
||||
return {points, indices};
|
||||
}
|
||||
|
||||
Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling,
|
||||
double floor_z_mm, double ceiling_z_mm,
|
||||
ThrowOnCancel thr)
|
||||
/// This function will return a triangulation of a sheet connecting an upper
|
||||
/// and a lower plate given as input polygons. It will not triangulate the
|
||||
/// plates themselves only the sheet. The caller has to specify the lower and
|
||||
/// upper z levels in world coordinates as well as the offset difference
|
||||
/// between the sheets. If the lower_z_mm is higher than upper_z_mm or the
|
||||
/// offset difference is negative, the resulting triangle orientation will be
|
||||
/// reversed.
|
||||
///
|
||||
/// IMPORTANT: This is not a universal triangulation algorithm. It assumes
|
||||
/// that the lower and upper polygons are offsetted versions of the same
|
||||
/// original polygon. In general, it assumes that one of the polygons is
|
||||
/// completely inside the other. The offset difference is the reference
|
||||
/// distance from the inner polygon's perimeter to the outer polygon's
|
||||
/// perimeter. The real distance will be variable as the clipper offset has
|
||||
/// different strategies (rounding, etc...). This algorithm should have
|
||||
/// O(2n + 3m) complexity where n is the number of upper vertices and m is the
|
||||
/// number of lower vertices.
|
||||
Contour3D walls(const Polygon& lower, const Polygon& upper,
|
||||
double lower_z_mm, double upper_z_mm,
|
||||
double offset_difference_mm, ThrowOnCancel thr)
|
||||
{
|
||||
using std::transform; using std::back_inserter;
|
||||
|
||||
ExPolygon poly;
|
||||
poly.contour.points = floor_plate.contour.points;
|
||||
poly.holes.emplace_back(ceiling.contour);
|
||||
auto& h = poly.holes.front();
|
||||
std::reverse(h.points.begin(), h.points.end());
|
||||
Polygons tri = triangulate(poly);
|
||||
|
||||
Contour3D ret;
|
||||
ret.points.reserve(tri.size() * 3);
|
||||
|
||||
double fz = floor_z_mm;
|
||||
double cz = ceiling_z_mm;
|
||||
auto& rp = ret.points;
|
||||
auto& rpi = ret.indices;
|
||||
ret.indices.reserve(tri.size() * 3);
|
||||
if(upper.points.size() < 3 || lower.size() < 3) return ret;
|
||||
|
||||
coord_t idx = 0;
|
||||
// The concept of the algorithm is relatively simple. It will try to find
|
||||
// the closest vertices from the upper and the lower polygon and use those
|
||||
// as starting points. Then it will create the triangles sequentially using
|
||||
// an edge from the upper polygon and a vertex from the lower or vice versa,
|
||||
// depending on the resulting triangle's quality.
|
||||
// The quality is measured by a scalar value. So far it looks like it is
|
||||
// enough to derive it from the slope of the triangle's two edges connecting
|
||||
// the upper and the lower part. A reference slope is calculated from the
|
||||
// height and the offset difference.
|
||||
|
||||
auto hlines = h.lines();
|
||||
auto is_upper = [&hlines](const Point& p) {
|
||||
return std::any_of(hlines.begin(), hlines.end(),
|
||||
[&p](const Line& l) {
|
||||
return l.distance_to(p) < mm(1e-6);
|
||||
});
|
||||
// Offset in the index array for the ceiling
|
||||
const auto offs = upper.points.size();
|
||||
|
||||
// Shorthand for the vertex arrays
|
||||
auto& upoints = upper.points, &lpoints = lower.points;
|
||||
auto& rpts = ret.points; auto& rfaces = ret.indices;
|
||||
|
||||
// If the Z levels are flipped, or the offset difference is negative, we
|
||||
// will interpret that as the triangles normals should be inverted.
|
||||
bool inverted = upper_z_mm < lower_z_mm || offset_difference_mm < 0;
|
||||
|
||||
// Copy the points into the mesh, convert them from 2D to 3D
|
||||
rpts.reserve(upoints.size() + lpoints.size());
|
||||
rfaces.reserve(2*upoints.size() + 2*lpoints.size());
|
||||
const double sf = SCALING_FACTOR;
|
||||
for(auto& p : upoints) rpts.emplace_back(p.x()*sf, p.y()*sf, upper_z_mm);
|
||||
for(auto& p : lpoints) rpts.emplace_back(p.x()*sf, p.y()*sf, lower_z_mm);
|
||||
|
||||
// Create pointing indices into vertex arrays. u-upper, l-lower
|
||||
size_t uidx = 0, lidx = offs, unextidx = 1, lnextidx = offs + 1;
|
||||
|
||||
// Simple squared distance calculation.
|
||||
auto distfn = [](const Vec3d& p1, const Vec3d& p2) {
|
||||
auto p = p1 - p2; return p.transpose() * p;
|
||||
};
|
||||
|
||||
std::for_each(tri.begin(), tri.end(),
|
||||
[&rp, &rpi, thr, &idx, is_upper, fz, cz](const Polygon& pp)
|
||||
{
|
||||
thr(); // may throw if cancellation was requested
|
||||
// We need to find the closest point on lower polygon to the first point on
|
||||
// the upper polygon. These will be our starting points.
|
||||
double distmin = std::numeric_limits<double>::max();
|
||||
for(size_t l = lidx; l < rpts.size(); ++l) {
|
||||
thr();
|
||||
double d = distfn(rpts[l], rpts[uidx]);
|
||||
if(d < distmin) { lidx = l; distmin = d; }
|
||||
}
|
||||
|
||||
for(auto& p : pp.points)
|
||||
if(is_upper(p))
|
||||
rp.emplace_back(unscale(x(p), y(p), mm(cz)));
|
||||
else rp.emplace_back(unscale(x(p), y(p), mm(fz)));
|
||||
// Set up lnextidx to be ahead of lidx in cyclic mode
|
||||
lnextidx = lidx + 1;
|
||||
if(lnextidx == rpts.size()) lnextidx = offs;
|
||||
|
||||
coord_t a = idx++, b = idx++, c = idx++;
|
||||
if(fz > cz) rpi.emplace_back(c, b, a);
|
||||
else rpi.emplace_back(a, b, c);
|
||||
});
|
||||
// This will be the flip switch to toggle between upper and lower triangle
|
||||
// creation mode
|
||||
enum class Proceed {
|
||||
UPPER, // A segment from the upper polygon and one vertex from the lower
|
||||
LOWER // A segment from the lower polygon and one vertex from the upper
|
||||
} proceed = Proceed::UPPER;
|
||||
|
||||
// Flags to help evaluating loop termination.
|
||||
bool ustarted = false, lstarted = false;
|
||||
|
||||
// The variables for the fitness values, one for the actual and one for the
|
||||
// previous.
|
||||
double current_fit = 0, prev_fit = 0;
|
||||
|
||||
// Every triangle of the wall has two edges connecting the upper plate with
|
||||
// the lower plate. From the length of these two edges and the zdiff we
|
||||
// can calculate the momentary squared offset distance at a particular
|
||||
// position on the wall. The average of the differences from the reference
|
||||
// (squared) offset distance will give us the driving fitness value.
|
||||
const double offsdiff2 = std::pow(offset_difference_mm, 2);
|
||||
const double zdiff2 = std::pow(upper_z_mm - lower_z_mm, 2);
|
||||
|
||||
// Mark the current vertex iterator positions. If the iterators return to
|
||||
// the same position, the loop can be terminated.
|
||||
size_t uendidx = uidx, lendidx = lidx;
|
||||
|
||||
do { thr(); // check throw if canceled
|
||||
|
||||
prev_fit = current_fit;
|
||||
|
||||
switch(proceed) { // proceed depending on the current state
|
||||
case Proceed::UPPER:
|
||||
if(!ustarted || uidx != uendidx) { // there are vertices remaining
|
||||
// Get the 3D vertices in order
|
||||
const Vec3d& p_up1 = rpts[size_t(uidx)];
|
||||
const Vec3d& p_low = rpts[size_t(lidx)];
|
||||
const Vec3d& p_up2 = rpts[size_t(unextidx)];
|
||||
|
||||
// Calculate fitness: the average of the two connecting edges
|
||||
double a = offsdiff2 - (distfn(p_up1, p_low) - zdiff2);
|
||||
double b = offsdiff2 - (distfn(p_up2, p_low) - zdiff2);
|
||||
current_fit = (std::abs(a) + std::abs(b)) / 2;
|
||||
|
||||
if(current_fit > prev_fit) { // fit is worse than previously
|
||||
proceed = Proceed::LOWER;
|
||||
} else { // good to go, create the triangle
|
||||
inverted? rfaces.emplace_back(unextidx, lidx, uidx) :
|
||||
rfaces.emplace_back(uidx, lidx, unextidx) ;
|
||||
|
||||
// Increment the iterators, rotate if necessary
|
||||
++uidx; ++unextidx;
|
||||
if(unextidx == offs) unextidx = 0;
|
||||
if(uidx == offs) uidx = 0;
|
||||
|
||||
ustarted = true; // mark the movement of the iterators
|
||||
// so that the comparison to uendidx can be made correctly
|
||||
}
|
||||
} else proceed = Proceed::LOWER;
|
||||
|
||||
break;
|
||||
case Proceed::LOWER:
|
||||
// Mode with lower segment, upper vertex. Same structure:
|
||||
if(!lstarted || lidx != lendidx) {
|
||||
const Vec3d& p_low1 = rpts[size_t(lidx)];
|
||||
const Vec3d& p_low2 = rpts[size_t(lnextidx)];
|
||||
const Vec3d& p_up = rpts[size_t(uidx)];
|
||||
|
||||
double a = offsdiff2 - (distfn(p_up, p_low1) - zdiff2);
|
||||
double b = offsdiff2 - (distfn(p_up, p_low2) - zdiff2);
|
||||
current_fit = (std::abs(a) + std::abs(b)) / 2;
|
||||
|
||||
if(current_fit > prev_fit) {
|
||||
proceed = Proceed::UPPER;
|
||||
} else {
|
||||
inverted? rfaces.emplace_back(uidx, lnextidx, lidx) :
|
||||
rfaces.emplace_back(lidx, lnextidx, uidx);
|
||||
|
||||
++lidx; ++lnextidx;
|
||||
if(lnextidx == rpts.size()) lnextidx = offs;
|
||||
if(lidx == rpts.size()) lidx = offs;
|
||||
|
||||
lstarted = true;
|
||||
}
|
||||
} else proceed = Proceed::UPPER;
|
||||
|
||||
break;
|
||||
} // end of switch
|
||||
} while(!ustarted || !lstarted || uidx != uendidx || lidx != lendidx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -207,20 +306,31 @@ ExPolygons unify(const ExPolygons& shapes) {
|
|||
/// Only a debug function to generate top and bottom plates from a 2D shape.
|
||||
/// It is not used in the algorithm directly.
|
||||
inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) {
|
||||
Polygons triangles = triangulate(poly);
|
||||
|
||||
auto lower = convert(triangles, 0, false);
|
||||
auto upper = convert(triangles, z_distance, true);
|
||||
lower.merge(upper);
|
||||
return lower;
|
||||
auto lower = triangulate_expolygon_3d(poly);
|
||||
auto upper = triangulate_expolygon_3d(poly, z_distance*SCALING_FACTOR, true);
|
||||
Contour3D ret;
|
||||
ret.merge(lower); ret.merge(upper);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// This method will create a rounded edge around a flat polygon in 3d space.
|
||||
/// 'base_plate' parameter is the target plate.
|
||||
/// 'radius' is the radius of the edges.
|
||||
/// 'degrees' is tells how much of a circle should be created as the rounding.
|
||||
/// It should be in degrees, not radians.
|
||||
/// 'ceilheight_mm' is the Z coordinate of the flat polygon in 3D space.
|
||||
/// 'dir' Is the direction of the round edges: inward or outward
|
||||
/// 'thr' Throws if a cancel signal was received
|
||||
/// 'last_offset' An auxiliary output variable to save the last offsetted
|
||||
/// version of 'base_plate'
|
||||
/// 'last_height' An auxiliary output to save the last z coordinate of the
|
||||
/// offsetted base_plate. In other words, where the rounded edges end.
|
||||
Contour3D round_edges(const ExPolygon& base_plate,
|
||||
double radius_mm,
|
||||
double degrees,
|
||||
double ceilheight_mm,
|
||||
bool dir,
|
||||
ThrowOnCancel throw_on_cancel,
|
||||
ThrowOnCancel thr,
|
||||
ExPolygon& last_offset, double& last_height)
|
||||
{
|
||||
auto ob = base_plate;
|
||||
|
|
@ -239,7 +349,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||
int(radius_mm*std::cos(degrees * PI / 180 - PI/2) / stepx) : steps;
|
||||
|
||||
for(int i = 1; i <= tos; ++i) {
|
||||
throw_on_cancel();
|
||||
thr();
|
||||
|
||||
ob = base_plate;
|
||||
|
||||
|
|
@ -252,7 +362,8 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||
wh = ceilheight_mm - radius_mm + stepy;
|
||||
|
||||
Contour3D pwalls;
|
||||
pwalls = walls(ob, ob_prev, wh, wh_prev, throw_on_cancel);
|
||||
double prev_x = xx - (i - 1) * stepx;
|
||||
pwalls = walls(ob.contour, ob_prev.contour, wh, wh_prev, s*prev_x, thr);
|
||||
|
||||
curvedwalls.merge(pwalls);
|
||||
ob_prev = ob;
|
||||
|
|
@ -264,7 +375,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||
int tos = int(tox / stepx);
|
||||
|
||||
for(int i = 1; i <= tos; ++i) {
|
||||
throw_on_cancel();
|
||||
thr();
|
||||
ob = base_plate;
|
||||
|
||||
double r2 = radius_mm * radius_mm;
|
||||
|
|
@ -275,7 +386,9 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||
wh = ceilheight_mm - radius_mm - stepy;
|
||||
|
||||
Contour3D pwalls;
|
||||
pwalls = walls(ob_prev, ob, wh_prev, wh, throw_on_cancel);
|
||||
double prev_x = xx - radius_mm + (i - 1)*stepx;
|
||||
pwalls =
|
||||
walls(ob_prev.contour, ob.contour, wh_prev, wh, s*prev_x, thr);
|
||||
|
||||
curvedwalls.merge(pwalls);
|
||||
ob_prev = ob;
|
||||
|
|
@ -291,15 +404,17 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||
|
||||
/// Generating the concave part of the 3D pool with the bottom plate and the
|
||||
/// side walls.
|
||||
Contour3D inner_bed(const ExPolygon& poly, double depth_mm,
|
||||
double begin_h_mm = 0) {
|
||||
|
||||
Polygons triangles = triangulate(poly);
|
||||
Contour3D inner_bed(const ExPolygon& poly,
|
||||
double depth_mm,
|
||||
double begin_h_mm = 0)
|
||||
{
|
||||
Contour3D bottom;
|
||||
Pointf3s triangles = triangulate_expolygon_3d(poly, -depth_mm + begin_h_mm);
|
||||
bottom.merge(triangles);
|
||||
|
||||
coord_t depth = mm(depth_mm);
|
||||
coord_t begin_h = mm(begin_h_mm);
|
||||
|
||||
auto bottom = convert(triangles, -depth + begin_h, false);
|
||||
auto lines = poly.lines();
|
||||
|
||||
// Generate outer walls
|
||||
|
|
@ -469,6 +584,9 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
|
|||
void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
||||
const PoolConfig& cfg)
|
||||
{
|
||||
// for debugging:
|
||||
// Benchmark bench;
|
||||
// bench.start();
|
||||
|
||||
double mergedist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm)+
|
||||
cfg.max_merge_distance_mm;
|
||||
|
|
@ -478,27 +596,28 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||
// serve as the bottom plate of the pad. We will offset this concave hull
|
||||
// and then offset back the result with clipper with rounding edges ON. This
|
||||
// trick will create a nice rounded pad shape.
|
||||
auto concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel);
|
||||
ExPolygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel);
|
||||
|
||||
const double thickness = cfg.min_wall_thickness_mm;
|
||||
const double wingheight = cfg.min_wall_height_mm;
|
||||
const double fullheight = wingheight + thickness;
|
||||
const double tilt = PI/4;
|
||||
const double tilt = cfg.wall_tilt;
|
||||
const double wingdist = wingheight / std::tan(tilt);
|
||||
|
||||
// scaled values
|
||||
const coord_t s_thickness = mm(thickness);
|
||||
const coord_t s_eradius = mm(cfg.edge_radius_mm);
|
||||
const coord_t s_safety_dist = 2*s_eradius + coord_t(0.8*s_thickness);
|
||||
// const coord_t wheight = mm(cfg.min_wall_height_mm);
|
||||
coord_t s_wingdist = mm(wingdist);
|
||||
const coord_t s_wingdist = mm(wingdist);
|
||||
|
||||
auto& thrcl = cfg.throw_on_cancel;
|
||||
|
||||
Contour3D pool;
|
||||
|
||||
for(ExPolygon& concaveh : concavehs) {
|
||||
if(concaveh.contour.points.empty()) return;
|
||||
|
||||
// Get rif of any holes in the concave hull output.
|
||||
// Get rid of any holes in the concave hull output.
|
||||
concaveh.holes.clear();
|
||||
|
||||
// Here lies the trick that does the smooting only with clipper offset
|
||||
|
|
@ -508,15 +627,22 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||
auto outer_base = concaveh;
|
||||
outer_base.holes.clear();
|
||||
offset(outer_base, s_safety_dist + s_wingdist + s_thickness);
|
||||
auto inner_base = outer_base;
|
||||
offset(inner_base, -(s_thickness + s_wingdist));
|
||||
|
||||
|
||||
ExPolygon bottom_poly = outer_base;
|
||||
bottom_poly.holes.clear();
|
||||
if(s_wingdist > 0) offset(bottom_poly, -s_wingdist);
|
||||
|
||||
// Punching a hole in the top plate for the cavity
|
||||
ExPolygon top_poly;
|
||||
ExPolygon middle_base;
|
||||
ExPolygon inner_base;
|
||||
top_poly.contour = outer_base.contour;
|
||||
|
||||
if(wingheight > 0) {
|
||||
inner_base = outer_base;
|
||||
offset(inner_base, -(s_thickness + s_wingdist + s_eradius));
|
||||
|
||||
middle_base = outer_base;
|
||||
offset(middle_base, -s_thickness);
|
||||
top_poly.holes.emplace_back(middle_base.contour);
|
||||
|
|
@ -524,8 +650,6 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||
std::reverse(tph.begin(), tph.end());
|
||||
}
|
||||
|
||||
Contour3D pool;
|
||||
|
||||
ExPolygon ob = outer_base; double wh = 0;
|
||||
|
||||
// now we will calculate the angle or portion of the circle from
|
||||
|
|
@ -557,60 +681,53 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||
|
||||
|
||||
// Generate the smoothed edge geometry
|
||||
auto walledges = round_edges(ob,
|
||||
pool.merge(round_edges(ob,
|
||||
r,
|
||||
phi,
|
||||
0, // z position of the input plane
|
||||
true,
|
||||
thrcl,
|
||||
ob, wh);
|
||||
pool.merge(walledges);
|
||||
ob, wh));
|
||||
|
||||
// Now that we have the rounded edge connencting the top plate with
|
||||
// Now that we have the rounded edge connecting the top plate with
|
||||
// the outer side walls, we can generate and merge the sidewall geometry
|
||||
auto pwalls = walls(ob, inner_base, wh, -fullheight, thrcl);
|
||||
pool.merge(pwalls);
|
||||
pool.merge(walls(ob.contour, bottom_poly.contour, wh, -fullheight,
|
||||
wingdist, thrcl));
|
||||
|
||||
if(wingheight > 0) {
|
||||
// Generate the smoothed edge geometry
|
||||
auto cavityedges = round_edges(middle_base,
|
||||
pool.merge(round_edges(middle_base,
|
||||
r,
|
||||
phi - 90, // from tangent lines
|
||||
0,
|
||||
0, // z position of the input plane
|
||||
false,
|
||||
thrcl,
|
||||
ob, wh);
|
||||
pool.merge(cavityedges);
|
||||
ob, wh));
|
||||
|
||||
// Next is the cavity walls connecting to the top plate's
|
||||
// artificially created hole.
|
||||
auto cavitywalls = walls(inner_base, ob, -wingheight, wh, thrcl);
|
||||
pool.merge(cavitywalls);
|
||||
pool.merge(walls(inner_base.contour, ob.contour, -wingheight,
|
||||
wh, -wingdist, thrcl));
|
||||
}
|
||||
|
||||
// Now we need to triangulate the top and bottom plates as well as the
|
||||
// cavity bottom plate which is the same as the bottom plate but it is
|
||||
// eleveted by the thickness.
|
||||
Polygons top_triangles, bottom_triangles;
|
||||
// elevated by the thickness.
|
||||
pool.merge(triangulate_expolygon_3d(top_poly));
|
||||
pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true));
|
||||
|
||||
triangulate(top_poly, top_triangles);
|
||||
triangulate(inner_base, bottom_triangles);
|
||||
if(wingheight > 0)
|
||||
pool.merge(triangulate_expolygon_3d(inner_base, -wingheight));
|
||||
|
||||
auto top_plate = convert(top_triangles, 0, false);
|
||||
auto bottom_plate = convert(bottom_triangles, -mm(fullheight), true);
|
||||
|
||||
pool.merge(top_plate);
|
||||
pool.merge(bottom_plate);
|
||||
|
||||
if(wingheight > 0) {
|
||||
Polygons middle_triangles;
|
||||
triangulate(inner_base, middle_triangles);
|
||||
auto middle_plate = convert(middle_triangles, -mm(wingheight), false);
|
||||
pool.merge(middle_plate);
|
||||
}
|
||||
|
||||
// For debugging:
|
||||
// bench.stop();
|
||||
// std::cout << "Pad creation time: " << bench.getElapsedSec() << std::endl;
|
||||
// std::fstream fout("pad_debug.obj", std::fstream::out);
|
||||
// if(fout.good()) pool.to_obj(fout);
|
||||
|
||||
out.merge(mesh(pool));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <cmath>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -27,15 +28,17 @@ struct PoolConfig {
|
|||
double min_wall_height_mm = 5;
|
||||
double max_merge_distance_mm = 50;
|
||||
double edge_radius_mm = 1;
|
||||
double wall_tilt = std::atan(1.0); // Universal constant for Pi/4
|
||||
|
||||
ThrowOnCancel throw_on_cancel = [](){};
|
||||
|
||||
inline PoolConfig() {}
|
||||
inline PoolConfig(double wt, double wh, double md, double er):
|
||||
inline PoolConfig(double wt, double wh, double md, double er, double tilt):
|
||||
min_wall_thickness_mm(wt),
|
||||
min_wall_height_mm(wh),
|
||||
max_merge_distance_mm(md),
|
||||
edge_radius_mm(er) {}
|
||||
edge_radius_mm(er),
|
||||
wall_tilt(tilt) {}
|
||||
};
|
||||
|
||||
/// Calculate the pool for the mesh for SLA printing
|
||||
|
|
|
|||
|
|
@ -36,14 +36,6 @@ inline coord_t x(const Vec3crd& p) { return p(0); }
|
|||
inline coord_t y(const Vec3crd& p) { return p(1); }
|
||||
inline coord_t z(const Vec3crd& p) { return p(2); }
|
||||
|
||||
inline void triangulate(const ExPolygon& expoly, Polygons& triangles) {
|
||||
expoly.triangulate_p2t(&triangles);
|
||||
}
|
||||
|
||||
inline Polygons triangulate(const ExPolygon& expoly) {
|
||||
Polygons tri; triangulate(expoly, tri); return tri;
|
||||
}
|
||||
|
||||
using Indices = std::vector<Vec3crd>;
|
||||
|
||||
/// Intermediate struct for a 3D mesh
|
||||
|
|
@ -63,6 +55,15 @@ struct Contour3D {
|
|||
}
|
||||
}
|
||||
|
||||
void merge(const Pointf3s& triangles) {
|
||||
const size_t offs = points.size();
|
||||
points.insert(points.end(), triangles.begin(), triangles.end());
|
||||
indices.reserve(indices.size() + points.size() / 3);
|
||||
|
||||
for(int i = (int)offs; i < (int)points.size(); i += 3)
|
||||
indices.emplace_back(i, i + 1, i + 2);
|
||||
}
|
||||
|
||||
// Write the index triangle structure to OBJ file for debugging purposes.
|
||||
void to_obj(std::ostream& stream) {
|
||||
for(auto& p : points) {
|
||||
|
|
@ -75,13 +76,9 @@ struct Contour3D {
|
|||
}
|
||||
};
|
||||
|
||||
//using PointSet = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign>; //Eigen::MatrixXd;
|
||||
using ClusterEl = std::vector<unsigned>;
|
||||
using ClusteredPoints = std::vector<ClusterEl>;
|
||||
|
||||
/// Convert the triangulation output to an intermediate mesh.
|
||||
Contour3D convert(const Polygons& triangles, coord_t z, bool dir);
|
||||
|
||||
/// Mesh from an existing contour.
|
||||
inline TriangleMesh mesh(const Contour3D& ctour) {
|
||||
return {ctour.points, ctour.indices};
|
||||
|
|
|
|||
137
src/libslic3r/SLA/SLACommon.hpp
Normal file
137
src/libslic3r/SLA/SLACommon.hpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#ifndef SLACOMMON_HPP
|
||||
#define SLACOMMON_HPP
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
// #define SLIC3R_SLA_NEEDS_WINDTREE
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Typedefs from Point.hpp
|
||||
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> Vec3f;
|
||||
typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d;
|
||||
|
||||
class TriangleMesh;
|
||||
|
||||
namespace sla {
|
||||
|
||||
struct SupportPoint {
|
||||
Vec3f pos;
|
||||
float head_front_radius;
|
||||
bool is_new_island;
|
||||
|
||||
SupportPoint() :
|
||||
pos(Vec3f::Zero()), head_front_radius(0.f), is_new_island(false) {}
|
||||
|
||||
SupportPoint(float pos_x, float pos_y, float pos_z, float head_radius, bool new_island) :
|
||||
pos(pos_x, pos_y, pos_z), head_front_radius(head_radius), is_new_island(new_island) {}
|
||||
|
||||
SupportPoint(Vec3f position, float head_radius, bool new_island) :
|
||||
pos(position), head_front_radius(head_radius), is_new_island(new_island) {}
|
||||
|
||||
SupportPoint(Eigen::Matrix<float, 5, 1, Eigen::DontAlign> data) :
|
||||
pos(data(0), data(1), data(2)), head_front_radius(data(3)), is_new_island(data(4) != 0.f) {}
|
||||
|
||||
bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; }
|
||||
bool operator!=(const SupportPoint& sp) const { return !(sp == (*this)); }
|
||||
};
|
||||
|
||||
|
||||
/// An index-triangle structure for libIGL functions. Also serves as an
|
||||
/// alternative (raw) input format for the SLASupportTree
|
||||
/*struct EigenMesh3D {
|
||||
Eigen::MatrixXd V;
|
||||
Eigen::MatrixXi F;
|
||||
double ground_level = 0;
|
||||
};*/
|
||||
|
||||
/// An index-triangle structure for libIGL functions. Also serves as an
|
||||
/// alternative (raw) input format for the SLASupportTree
|
||||
class EigenMesh3D {
|
||||
class AABBImpl;
|
||||
|
||||
Eigen::MatrixXd m_V;
|
||||
Eigen::MatrixXi m_F;
|
||||
double m_ground_level = 0;
|
||||
|
||||
std::unique_ptr<AABBImpl> m_aabb;
|
||||
public:
|
||||
|
||||
EigenMesh3D(const TriangleMesh&);
|
||||
EigenMesh3D(const EigenMesh3D& other);
|
||||
EigenMesh3D& operator=(const EigenMesh3D&);
|
||||
|
||||
~EigenMesh3D();
|
||||
|
||||
inline double ground_level() const { return m_ground_level; }
|
||||
|
||||
inline const Eigen::MatrixXd& V() const { return m_V; }
|
||||
inline const Eigen::MatrixXi& F() const { return m_F; }
|
||||
|
||||
// Result of a raycast
|
||||
class hit_result {
|
||||
double m_t = std::numeric_limits<double>::infinity();
|
||||
int m_face_id = -1;
|
||||
const EigenMesh3D& m_mesh;
|
||||
Vec3d m_dir;
|
||||
inline hit_result(const EigenMesh3D& em): m_mesh(em) {}
|
||||
friend class EigenMesh3D;
|
||||
public:
|
||||
|
||||
inline double distance() const { return m_t; }
|
||||
inline const Vec3d& direction() const { return m_dir; }
|
||||
inline int face() const { return m_face_id; }
|
||||
|
||||
inline Vec3d normal() const {
|
||||
if(m_face_id < 0) return {};
|
||||
auto trindex = m_mesh.m_F.row(m_face_id);
|
||||
const Vec3d& p1 = m_mesh.V().row(trindex(0));
|
||||
const Vec3d& p2 = m_mesh.V().row(trindex(1));
|
||||
const Vec3d& p3 = m_mesh.V().row(trindex(2));
|
||||
Eigen::Vector3d U = p2 - p1;
|
||||
Eigen::Vector3d V = p3 - p1;
|
||||
return U.cross(V).normalized();
|
||||
}
|
||||
|
||||
inline bool is_inside() {
|
||||
return m_face_id >= 0 && normal().dot(m_dir) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Casting a ray on the mesh, returns the distance where the hit occures.
|
||||
hit_result query_ray_hit(const Vec3d &s, const Vec3d &dir) const;
|
||||
|
||||
class si_result {
|
||||
double m_value;
|
||||
int m_fidx;
|
||||
Vec3d m_p;
|
||||
si_result(double val, int i, const Vec3d& c):
|
||||
m_value(val), m_fidx(i), m_p(c) {}
|
||||
friend class EigenMesh3D;
|
||||
public:
|
||||
|
||||
si_result() = delete;
|
||||
|
||||
double value() const { return m_value; }
|
||||
operator double() const { return m_value; }
|
||||
const Vec3d& point_on_mesh() const { return m_p; }
|
||||
int F_idx() const { return m_fidx; }
|
||||
};
|
||||
|
||||
#ifdef SLIC3R_SLA_NEEDS_WINDTREE
|
||||
// The signed distance from a point to the mesh. Outputs the distance,
|
||||
// the index of the triangle and the closest point in mesh coordinate space.
|
||||
si_result signed_distance(const Vec3d& p) const;
|
||||
|
||||
bool inside(const Vec3d& p) const;
|
||||
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace sla
|
||||
} // namespace Slic3r
|
||||
|
||||
|
||||
#endif // SLASUPPORTTREE_HPP
|
||||
|
|
@ -551,10 +551,16 @@ enum { // For indexing Eigen vectors as v(X), v(Y), v(Z) instead of numbers
|
|||
X, Y, Z
|
||||
};
|
||||
|
||||
PointSet to_point_set(const std::vector<Vec3d> &v)
|
||||
PointSet to_point_set(const std::vector<SupportPoint> &v)
|
||||
{
|
||||
PointSet ret(v.size(), 3);
|
||||
{ long i = 0; for(const Vec3d& p : v) ret.row(i++) = p; }
|
||||
long i = 0;
|
||||
for(const SupportPoint& support_point : v) {
|
||||
ret.row(i)(0) = support_point.pos(0);
|
||||
ret.row(i)(1) = support_point.pos(1);
|
||||
ret.row(i)(2) = support_point.pos(2);
|
||||
++i;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -679,6 +685,7 @@ double pinhead_mesh_intersect(const Vec3d& s,
|
|||
return *mit;
|
||||
}
|
||||
|
||||
|
||||
// Checking bridge (pillar and stick as well) intersection with the model. If
|
||||
// the function is used for headless sticks, the ins_check parameter have to be
|
||||
// true as the beginning of the stick might be inside the model geometry.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
#include <memory>
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include "SLACommon.hpp"
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Needed types from Point.hpp
|
||||
|
|
@ -105,86 +108,6 @@ struct Controller {
|
|||
std::function<void(void)> cancelfn = [](){};
|
||||
};
|
||||
|
||||
/// An index-triangle structure for libIGL functions. Also serves as an
|
||||
/// alternative (raw) input format for the SLASupportTree
|
||||
class EigenMesh3D {
|
||||
class AABBImpl;
|
||||
|
||||
Eigen::MatrixXd m_V;
|
||||
Eigen::MatrixXi m_F;
|
||||
double m_ground_level = 0;
|
||||
|
||||
std::unique_ptr<AABBImpl> m_aabb;
|
||||
public:
|
||||
|
||||
EigenMesh3D(const TriangleMesh&);
|
||||
EigenMesh3D(const EigenMesh3D& other);
|
||||
EigenMesh3D& operator=(const EigenMesh3D&);
|
||||
|
||||
~EigenMesh3D();
|
||||
|
||||
inline double ground_level() const { return m_ground_level; }
|
||||
|
||||
inline const Eigen::MatrixXd& V() const { return m_V; }
|
||||
inline const Eigen::MatrixXi& F() const { return m_F; }
|
||||
|
||||
// Result of a raycast
|
||||
class hit_result {
|
||||
double m_t = std::numeric_limits<double>::infinity();
|
||||
int m_face_id = -1;
|
||||
const EigenMesh3D& m_mesh;
|
||||
Vec3d m_dir;
|
||||
inline hit_result(const EigenMesh3D& em): m_mesh(em) {}
|
||||
friend class EigenMesh3D;
|
||||
public:
|
||||
|
||||
inline double distance() const { return m_t; }
|
||||
|
||||
inline int face() const { return m_face_id; }
|
||||
|
||||
inline Vec3d normal() const {
|
||||
if(m_face_id < 0) return {};
|
||||
auto trindex = m_mesh.m_F.row(m_face_id);
|
||||
const Vec3d& p1 = m_mesh.V().row(trindex(0));
|
||||
const Vec3d& p2 = m_mesh.V().row(trindex(1));
|
||||
const Vec3d& p3 = m_mesh.V().row(trindex(2));
|
||||
Eigen::Vector3d U = p2 - p1;
|
||||
Eigen::Vector3d V = p3 - p1;
|
||||
return U.cross(V).normalized();
|
||||
}
|
||||
|
||||
inline bool is_inside() {
|
||||
return m_face_id >= 0 && normal().dot(m_dir) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Casting a ray on the mesh, returns the distance where the hit occures.
|
||||
hit_result query_ray_hit(const Vec3d &s, const Vec3d &dir) const;
|
||||
|
||||
class si_result {
|
||||
double m_value;
|
||||
int m_fidx;
|
||||
Vec3d m_p;
|
||||
si_result(double val, int i, const Vec3d& c):
|
||||
m_value(val), m_fidx(i), m_p(c) {}
|
||||
friend class EigenMesh3D;
|
||||
public:
|
||||
|
||||
si_result() = delete;
|
||||
|
||||
double value() const { return m_value; }
|
||||
operator double() const { return m_value; }
|
||||
const Vec3d& point_on_mesh() const { return m_p; }
|
||||
int F_idx() const { return m_fidx; }
|
||||
};
|
||||
|
||||
// The signed distance from a point to the mesh. Outputs the distance,
|
||||
// the index of the triangle and the closest point in mesh coordinate space.
|
||||
si_result signed_distance(const Vec3d& p) const;
|
||||
|
||||
bool inside(const Vec3d& p) const;
|
||||
};
|
||||
|
||||
using PointSet = Eigen::MatrixXd;
|
||||
|
||||
//EigenMesh3D to_eigenmesh(const TriangleMesh& m);
|
||||
|
|
@ -193,7 +116,7 @@ using PointSet = Eigen::MatrixXd;
|
|||
//EigenMesh3D to_eigenmesh(const ModelObject& model);
|
||||
|
||||
// Simple conversion of 'vector of points' to an Eigen matrix
|
||||
PointSet to_point_set(const std::vector<Vec3d>&);
|
||||
PointSet to_point_set(const std::vector<sla::SupportPoint>&);
|
||||
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
|
|
|||
|
|
@ -95,7 +95,9 @@ size_t SpatIndex::size() const
|
|||
|
||||
class EigenMesh3D::AABBImpl: public igl::AABB<Eigen::MatrixXd, 3> {
|
||||
public:
|
||||
#ifdef SLIC3R_SLA_NEEDS_WINDTREE
|
||||
igl::WindingNumberAABB<Vec3d, Eigen::MatrixXd, Eigen::MatrixXi> windtree;
|
||||
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
|
||||
};
|
||||
|
||||
EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) {
|
||||
|
|
@ -136,7 +138,9 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) {
|
|||
|
||||
// Build the AABB accelaration tree
|
||||
m_aabb->init(m_V, m_F);
|
||||
#ifdef SLIC3R_SLA_NEEDS_WINDTREE
|
||||
m_aabb->windtree.set_mesh(m_V, m_F);
|
||||
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
|
||||
}
|
||||
|
||||
EigenMesh3D::~EigenMesh3D() {}
|
||||
|
|
@ -168,6 +172,7 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_SLA_NEEDS_WINDTREE
|
||||
EigenMesh3D::si_result EigenMesh3D::signed_distance(const Vec3d &p) const {
|
||||
double sign = 0; double sqdst = 0; int i = 0; Vec3d c;
|
||||
igl::signed_distance_winding_number(*m_aabb, m_V, m_F, m_aabb->windtree,
|
||||
|
|
@ -179,6 +184,7 @@ EigenMesh3D::si_result EigenMesh3D::signed_distance(const Vec3d &p) const {
|
|||
bool EigenMesh3D::inside(const Vec3d &p) const {
|
||||
return m_aabb->windtree.inside(p);
|
||||
}
|
||||
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
|
||||
|
||||
/* ****************************************************************************
|
||||
* Misc functions
|
||||
|
|
@ -199,9 +205,11 @@ template<class Vec> double distance(const Vec& pp1, const Vec& pp2) {
|
|||
return std::sqrt(p.transpose() * p);
|
||||
}
|
||||
|
||||
PointSet normals(const PointSet& points, const EigenMesh3D& mesh,
|
||||
PointSet normals(const PointSet& points,
|
||||
const EigenMesh3D& mesh,
|
||||
double eps,
|
||||
std::function<void()> throw_on_cancel) {
|
||||
std::function<void()> throw_on_cancel)
|
||||
{
|
||||
if(points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0)
|
||||
return {};
|
||||
|
||||
|
|
@ -222,7 +230,7 @@ PointSet normals(const PointSet& points, const EigenMesh3D& mesh,
|
|||
const Vec3d& p3 = mesh.V().row(trindex(2));
|
||||
|
||||
// We should check if the point lies on an edge of the hosting triangle.
|
||||
// If it does than all the other triangles using the same two points
|
||||
// If it does then all the other triangles using the same two points
|
||||
// have to be searched and the final normal should be some kind of
|
||||
// aggregation of the participating triangle normals. We should also
|
||||
// consider the cases where the support point lies right on a vertex
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@
|
|||
#include "SLA/SLASupportTree.hpp"
|
||||
#include "SLA/SLABasePool.hpp"
|
||||
#include "SLA/SLAAutoSupports.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <numeric>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
//#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
|
||||
|
|
@ -25,7 +27,7 @@ using SupportTreePtr = std::unique_ptr<sla::SLASupportTree>;
|
|||
class SLAPrintObject::SupportData {
|
||||
public:
|
||||
sla::EigenMesh3D emesh; // index-triangle representation
|
||||
sla::PointSet support_points; // all the support points (manual/auto)
|
||||
std::vector<sla::SupportPoint> support_points; // all the support points (manual/auto)
|
||||
SupportTreePtr support_tree_ptr; // the supports
|
||||
SlicedSupports support_slices; // sliced supports
|
||||
std::vector<LevelID> level_ids;
|
||||
|
|
@ -51,7 +53,7 @@ const std::array<std::string, slaposCount> OBJ_STEP_LABELS =
|
|||
L("Slicing model"), // slaposObjectSlice,
|
||||
L("Generating support points"), // slaposSupportPoints,
|
||||
L("Generating support tree"), // slaposSupportTree,
|
||||
L("Generating base pool"), // slaposBasePool,
|
||||
L("Generating pad"), // slaposBasePool,
|
||||
L("Slicing supports"), // slaposSliceSupports,
|
||||
L("Slicing supports") // slaposIndexSlices,
|
||||
};
|
||||
|
|
@ -182,7 +184,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
|||
if (model.id() != m_model.id()) {
|
||||
// Kill everything, initialize from scratch.
|
||||
// Stop background processing.
|
||||
this->call_cancell_callback();
|
||||
this->call_cancel_callback();
|
||||
update_apply_status(this->invalidate_all_steps());
|
||||
for (SLAPrintObject *object : m_objects) {
|
||||
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
|
||||
|
|
@ -211,7 +213,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
|||
} else {
|
||||
// Reorder the objects, add new objects.
|
||||
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
|
||||
this->call_cancell_callback();
|
||||
this->call_cancel_callback();
|
||||
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||
// Second create a new list of objects.
|
||||
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
|
||||
|
|
@ -308,7 +310,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
|||
if (it_print_object_status != print_object_status.end() && it_print_object_status->id != model_object.id())
|
||||
it_print_object_status = print_object_status.end();
|
||||
// Check whether a model part volume was added or removed, their transformations or order changed.
|
||||
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::MODEL_PART);
|
||||
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
|
||||
bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() ||
|
||||
(! model_object.instances.empty() && ! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)));
|
||||
if (model_parts_differ || sla_trafo_differs) {
|
||||
|
|
@ -354,6 +356,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
|||
std::vector<SLAPrintObject::Instance> new_instances = sla_instances(model_object);
|
||||
if (it_print_object_status != print_object_status.end() && it_print_object_status->status != PrintObjectStatus::Deleted) {
|
||||
// The SLAPrintObject is already there.
|
||||
if (new_instances.empty()) {
|
||||
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Deleted;
|
||||
} else {
|
||||
if (new_instances != it_print_object_status->print_object->instances()) {
|
||||
// Instances changed.
|
||||
it_print_object_status->print_object->set_instances(new_instances);
|
||||
|
|
@ -361,7 +366,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
|||
}
|
||||
print_objects_new.emplace_back(it_print_object_status->print_object);
|
||||
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Reused;
|
||||
} else {
|
||||
}
|
||||
} else if (! new_instances.empty()) {
|
||||
auto print_object = new SLAPrintObject(this, &model_object);
|
||||
|
||||
// FIXME: this invalidates the transformed mesh in SLAPrintObject
|
||||
|
|
@ -376,7 +382,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
|||
}
|
||||
|
||||
if (m_objects != print_objects_new) {
|
||||
this->call_cancell_callback();
|
||||
this->call_cancel_callback();
|
||||
update_apply_status(this->invalidate_all_steps());
|
||||
m_objects = print_objects_new;
|
||||
// Delete the PrintObjects marked as Unknown or Deleted.
|
||||
|
|
@ -398,6 +404,113 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
|||
return static_cast<ApplyStatus>(apply_status);
|
||||
}
|
||||
|
||||
// After calling the apply() function, set_task() may be called to limit the task to be processed by process().
|
||||
void SLAPrint::set_task(const TaskParams ¶ms)
|
||||
{
|
||||
// Grab the lock for the Print / PrintObject milestones.
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
|
||||
int n_object_steps = int(params.to_object_step) + 1;
|
||||
if (n_object_steps == 0)
|
||||
n_object_steps = (int)slaposCount;
|
||||
|
||||
if (params.single_model_object.valid()) {
|
||||
// Find the print object to be processed with priority.
|
||||
SLAPrintObject *print_object = nullptr;
|
||||
size_t idx_print_object = 0;
|
||||
for (; idx_print_object < m_objects.size(); ++ idx_print_object)
|
||||
if (m_objects[idx_print_object]->model_object()->id() == params.single_model_object) {
|
||||
print_object = m_objects[idx_print_object];
|
||||
break;
|
||||
}
|
||||
assert(print_object != nullptr);
|
||||
// Find out whether the priority print object is being currently processed.
|
||||
bool running = false;
|
||||
for (int istep = 0; istep < n_object_steps; ++ istep) {
|
||||
if (! print_object->m_stepmask[istep])
|
||||
// Step was skipped, cancel.
|
||||
break;
|
||||
if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) {
|
||||
// No step was skipped, and a wanted step is being processed. Don't cancel.
|
||||
running = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! running)
|
||||
this->call_cancel_callback();
|
||||
|
||||
// Now the background process is either stopped, or it is inside one of the print object steps to be calculated anyway.
|
||||
if (params.single_model_instance_only) {
|
||||
// Suppress all the steps of other instances.
|
||||
for (SLAPrintObject *po : m_objects)
|
||||
for (int istep = 0; istep < (int)slaposCount; ++ istep)
|
||||
po->m_stepmask[istep] = false;
|
||||
} else if (! running) {
|
||||
// Swap the print objects, so that the selected print_object is first in the row.
|
||||
// At this point the background processing must be stopped, so it is safe to shuffle print objects.
|
||||
if (idx_print_object != 0)
|
||||
std::swap(m_objects.front(), m_objects[idx_print_object]);
|
||||
}
|
||||
// and set the steps for the current object.
|
||||
for (int istep = 0; istep < n_object_steps; ++ istep)
|
||||
print_object->m_stepmask[istep] = true;
|
||||
for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
|
||||
print_object->m_stepmask[istep] = false;
|
||||
} else {
|
||||
// Slicing all objects.
|
||||
bool running = false;
|
||||
for (SLAPrintObject *print_object : m_objects)
|
||||
for (int istep = 0; istep < n_object_steps; ++ istep) {
|
||||
if (! print_object->m_stepmask[istep]) {
|
||||
// Step may have been skipped. Restart.
|
||||
goto loop_end;
|
||||
}
|
||||
if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) {
|
||||
// This step is running, and the state cannot be changed due to the this->state_mutex() being locked.
|
||||
// It is safe to manipulate m_stepmask of other SLAPrintObjects and SLAPrint now.
|
||||
running = true;
|
||||
goto loop_end;
|
||||
}
|
||||
}
|
||||
loop_end:
|
||||
if (! running)
|
||||
this->call_cancel_callback();
|
||||
for (SLAPrintObject *po : m_objects) {
|
||||
for (int istep = 0; istep < n_object_steps; ++ istep)
|
||||
po->m_stepmask[istep] = true;
|
||||
for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
|
||||
po->m_stepmask[istep] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.to_object_step != -1 || params.to_print_step != -1) {
|
||||
// Limit the print steps.
|
||||
size_t istep = (params.to_object_step != -1) ? 0 : size_t(params.to_print_step) + 1;
|
||||
for (; istep < m_stepmask.size(); ++ istep)
|
||||
m_stepmask[istep] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up after process() finished, either with success, error or if canceled.
|
||||
// The adjustments on the SLAPrint / SLAPrintObject data due to set_task() are to be reverted here.
|
||||
void SLAPrint::finalize()
|
||||
{
|
||||
for (SLAPrintObject *po : m_objects)
|
||||
for (int istep = 0; istep < (int)slaposCount; ++ istep)
|
||||
po->m_stepmask[istep] = true;
|
||||
for (int istep = 0; istep < (int)slapsCount; ++ istep)
|
||||
m_stepmask[istep] = true;
|
||||
}
|
||||
|
||||
// Generate a recommended output file name based on the format template, default extension, and template parameters
|
||||
// (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics.
|
||||
// Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before the output is finalized).
|
||||
std::string SLAPrint::output_filename() const
|
||||
{
|
||||
DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
|
||||
return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "zip", &config);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Compile the argument for support creation from the static print config.
|
||||
sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
|
||||
|
|
@ -472,7 +585,7 @@ void SLAPrint::process()
|
|||
const size_t objcount = m_objects.size();
|
||||
|
||||
const unsigned min_objstatus = 0; // where the per object operations start
|
||||
const unsigned max_objstatus = 80; // where the per object operations end
|
||||
const unsigned max_objstatus = PRINT_STEP_LEVELS[slapsRasterize]; // where the per object operations end
|
||||
|
||||
// the coefficient that multiplies the per object status values which
|
||||
// are set up for <0, 100>. They need to be scaled into the whole process
|
||||
|
|
@ -532,9 +645,8 @@ void SLAPrint::process()
|
|||
this->throw_if_canceled();
|
||||
SLAAutoSupports::Config config;
|
||||
const SLAPrintObjectConfig& cfg = po.config();
|
||||
config.minimal_z = float(cfg.support_minimal_z);
|
||||
config.density_at_45 = cfg.support_density_at_45 / 10000.f;
|
||||
config.density_at_horizontal = cfg.support_density_at_horizontal / 10000.f;
|
||||
config.density_relative = float(cfg.support_points_density_relative / 100.f); // the config value is in percents
|
||||
config.minimal_distance = float(cfg.support_points_minimal_distance);
|
||||
|
||||
// Construction of this object does the calculation.
|
||||
this->throw_if_canceled();
|
||||
|
|
@ -546,17 +658,19 @@ void SLAPrint::process()
|
|||
[this]() { throw_if_canceled(); });
|
||||
|
||||
// Now let's extract the result.
|
||||
const std::vector<Vec3d>& points = auto_supports.output();
|
||||
const std::vector<sla::SupportPoint>& points = auto_supports.output();
|
||||
this->throw_if_canceled();
|
||||
po.m_supportdata->support_points = sla::to_point_set(points);
|
||||
po.m_supportdata->support_points = points;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Automatic support points: "
|
||||
<< po.m_supportdata->support_points.rows();
|
||||
<< po.m_supportdata->support_points.size();
|
||||
|
||||
// Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass the update status to GLGizmoSlaSupports
|
||||
report_status(*this, -1, L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
|
||||
}
|
||||
else {
|
||||
// There are some points on the front-end, no calculation will be done.
|
||||
po.m_supportdata->support_points =
|
||||
sla::to_point_set(po.transformed_support_points());
|
||||
po.m_supportdata->support_points = po.transformed_support_points();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -587,6 +701,8 @@ void SLAPrint::process()
|
|||
|
||||
ctl.statuscb = [this, init, d](unsigned st, const std::string& msg)
|
||||
{
|
||||
//FIXME this status line scaling does not seem to be correct.
|
||||
// How does it account for an increasing object index?
|
||||
report_status(*this, int(init + st*d), msg);
|
||||
};
|
||||
|
||||
|
|
@ -594,7 +710,7 @@ void SLAPrint::process()
|
|||
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
||||
|
||||
po.m_supportdata->support_tree_ptr.reset(
|
||||
new SLASupportTree(po.m_supportdata->support_points,
|
||||
new SLASupportTree(sla::to_point_set(po.m_supportdata->support_points),
|
||||
po.m_supportdata->emesh, scfg, ctl));
|
||||
|
||||
// Create the unified mesh
|
||||
|
|
@ -605,7 +721,7 @@ void SLAPrint::process()
|
|||
po.m_supportdata->support_tree_ptr->merged_mesh();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Processed support point count "
|
||||
<< po.m_supportdata->support_points.rows();
|
||||
<< po.m_supportdata->support_points.size();
|
||||
|
||||
// Check the mesh for later troubleshooting.
|
||||
if(po.support_mesh().empty())
|
||||
|
|
@ -635,11 +751,13 @@ void SLAPrint::process()
|
|||
double wt = po.m_config.pad_wall_thickness.getFloat();
|
||||
double h = po.m_config.pad_wall_height.getFloat();
|
||||
double md = po.m_config.pad_max_merge_distance.getFloat();
|
||||
double er = po.m_config.pad_edge_radius.getFloat();
|
||||
// Radius is disabled for now...
|
||||
double er = 0; // po.m_config.pad_edge_radius.getFloat();
|
||||
double tilt = po.m_config.pad_wall_tilt.getFloat() * PI / 180.0;
|
||||
double lh = po.m_config.layer_height.getFloat();
|
||||
double elevation = po.m_config.support_object_elevation.getFloat();
|
||||
if(!po.m_config.supports_enable.getBool()) elevation = 0;
|
||||
sla::PoolConfig pcfg(wt, h, md, er);
|
||||
sla::PoolConfig pcfg(wt, h, md, er, tilt);
|
||||
|
||||
ExPolygons bp;
|
||||
double pad_h = sla::get_pad_fullheight(pcfg);
|
||||
|
|
@ -650,8 +768,7 @@ void SLAPrint::process()
|
|||
|
||||
if(elevation < pad_h) {
|
||||
// we have to count with the model geometry for the base plate
|
||||
sla::base_plate(trmesh, bp, float(pad_h), float(lh),
|
||||
thrfn);
|
||||
sla::base_plate(trmesh, bp, float(pad_h), float(lh), thrfn);
|
||||
}
|
||||
|
||||
pcfg.throw_on_cancel = thrfn;
|
||||
|
|
@ -878,21 +995,20 @@ void SLAPrint::process()
|
|||
|
||||
// Print all the layers in parallel
|
||||
tbb::parallel_for<unsigned, decltype(lvlfn)>(0, lvlcnt, lvlfn);
|
||||
|
||||
// Fill statistics
|
||||
this->fill_statistics();
|
||||
// Set statistics values to the printer
|
||||
m_printer->set_statistics({(m_print_statistics.objects_used_material + m_print_statistics.support_used_material)/1000,
|
||||
double(m_default_object_config.faded_layers.getInt()),
|
||||
double(m_print_statistics.slow_layers_count),
|
||||
double(m_print_statistics.fast_layers_count)
|
||||
});
|
||||
};
|
||||
|
||||
using slaposFn = std::function<void(SLAPrintObject&)>;
|
||||
using slapsFn = std::function<void(void)>;
|
||||
|
||||
// This is the actual order of steps done on each PrintObject
|
||||
std::array<SLAPrintObjectStep, slaposCount> objectsteps = {
|
||||
slaposObjectSlice, // SupportPoints will need this step
|
||||
slaposSupportPoints,
|
||||
slaposSupportTree,
|
||||
slaposBasePool,
|
||||
slaposSliceSupports,
|
||||
slaposIndexSlices
|
||||
};
|
||||
|
||||
std::array<slaposFn, slaposCount> pobj_program =
|
||||
{
|
||||
slice_model,
|
||||
|
|
@ -916,12 +1032,15 @@ void SLAPrint::process()
|
|||
|
||||
// TODO: this loop could run in parallel but should not exhaust all the CPU
|
||||
// power available
|
||||
// Calculate the support structures first before slicing the supports, so that the preview will get displayed ASAP for all objects.
|
||||
std::vector<SLAPrintObjectStep> step_ranges = { slaposObjectSlice, slaposSliceSupports, slaposCount };
|
||||
for (size_t idx_range = 0; idx_range + 1 < step_ranges.size(); ++ idx_range) {
|
||||
for(SLAPrintObject * po : m_objects) {
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing object " << po->model_object()->name;
|
||||
|
||||
for(size_t s = 0; s < objectsteps.size(); ++s) {
|
||||
auto currentstep = objectsteps[s];
|
||||
for (int s = (int)step_ranges[idx_range]; s < (int)step_ranges[idx_range + 1]; ++s) {
|
||||
auto currentstep = (SLAPrintObjectStep)s;
|
||||
|
||||
// Cancellation checking. Each step will check for cancellation
|
||||
// on its own and return earlier gracefully. Just after it returns
|
||||
|
|
@ -940,6 +1059,7 @@ void SLAPrint::process()
|
|||
incr = OBJ_STEP_LEVELS[currentstep];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::array<SLAPrintStep, slapsCount> printsteps = {
|
||||
slapsRasterize, slapsValidate
|
||||
|
|
@ -994,7 +1114,10 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
|
|||
"bed_shape",
|
||||
"max_print_height",
|
||||
"printer_technology",
|
||||
"output_filename_format"
|
||||
"output_filename_format",
|
||||
"fast_tilt_time",
|
||||
"slow_tilt_time",
|
||||
"area_fill"
|
||||
};
|
||||
|
||||
std::vector<SLAPrintStep> steps;
|
||||
|
|
@ -1027,6 +1150,166 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
|
|||
return invalidated;
|
||||
}
|
||||
|
||||
void SLAPrint::fill_statistics()
|
||||
{
|
||||
const double init_layer_height = m_material_config.initial_layer_height.getFloat();
|
||||
const double layer_height = m_default_object_config.layer_height.getFloat();
|
||||
|
||||
const double area_fill = m_printer_config.area_fill.getFloat()*0.01;// 0.5 (50%);
|
||||
const double fast_tilt = m_printer_config.fast_tilt_time.getFloat();// 5.0;
|
||||
const double slow_tilt = m_printer_config.slow_tilt_time.getFloat();// 8.0;
|
||||
|
||||
const double init_exp_time = m_material_config.initial_exposure_time.getFloat();
|
||||
const double exp_time = m_material_config.exposure_time.getFloat();
|
||||
|
||||
const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20]
|
||||
|
||||
const double width = m_printer_config.display_width.getFloat() / SCALING_FACTOR;
|
||||
const double height = m_printer_config.display_height.getFloat() / SCALING_FACTOR;
|
||||
const double display_area = width*height;
|
||||
|
||||
// get polygons for all instances in the object
|
||||
auto get_all_polygons = [](const ExPolygons& input_polygons, const std::vector<SLAPrintObject::Instance>& instances) {
|
||||
const size_t inst_cnt = instances.size();
|
||||
|
||||
size_t polygon_cnt = 0;
|
||||
for (const ExPolygon& polygon : input_polygons)
|
||||
polygon_cnt += polygon.holes.size() + 1;
|
||||
|
||||
Polygons polygons;
|
||||
polygons.reserve(polygon_cnt * inst_cnt);
|
||||
for (const ExPolygon& polygon : input_polygons) {
|
||||
for (size_t i = 0; i < inst_cnt; ++i)
|
||||
{
|
||||
ExPolygon tmp = polygon;
|
||||
tmp.rotate(Geometry::rad2deg(instances[i].rotation));
|
||||
tmp.translate(instances[i].shift.x(), instances[i].shift.y());
|
||||
polygons_append(polygons, to_polygons(std::move(tmp)));
|
||||
}
|
||||
}
|
||||
return polygons;
|
||||
};
|
||||
|
||||
double supports_volume = 0.0;
|
||||
double models_volume = 0.0;
|
||||
|
||||
double estim_time = 0.0;
|
||||
|
||||
size_t slow_layers = 0;
|
||||
size_t fast_layers = 0;
|
||||
|
||||
// find highest object
|
||||
// Which is a better bet? To compare by max_z or by number of layers in the index?
|
||||
double max_z = 0.;
|
||||
size_t max_layers_cnt = 0;
|
||||
size_t highest_obj_idx = 0;
|
||||
for (SLAPrintObject *&po : m_objects) {
|
||||
const SLAPrintObject::SliceIndex& slice_index = po->get_slice_index();
|
||||
if (! slice_index.empty()) {
|
||||
double z = (-- slice_index.end())->first;
|
||||
size_t cnt = slice_index.size();
|
||||
//if (z > max_z) {
|
||||
if (cnt > max_layers_cnt) {
|
||||
max_layers_cnt = cnt;
|
||||
max_z = z;
|
||||
highest_obj_idx = &po - &m_objects.front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SLAPrintObject * highest_obj = m_objects[highest_obj_idx];
|
||||
const SLAPrintObject::SliceIndex& highest_obj_slice_index = highest_obj->get_slice_index();
|
||||
|
||||
const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1);
|
||||
double fade_layer_time = init_exp_time;
|
||||
|
||||
int sliced_layer_cnt = 0;
|
||||
for (const auto& layer : highest_obj_slice_index)
|
||||
{
|
||||
const double l_height = (layer.first == highest_obj_slice_index.begin()->first) ? init_layer_height : layer_height;
|
||||
|
||||
// Calculation of the consumed material
|
||||
|
||||
Polygons model_polygons;
|
||||
Polygons supports_polygons;
|
||||
|
||||
for (SLAPrintObject * po : m_objects)
|
||||
{
|
||||
const SLAPrintObject::SliceRecord *record = nullptr;
|
||||
{
|
||||
const SLAPrintObject::SliceIndex& index = po->get_slice_index();
|
||||
auto key = layer.first;
|
||||
const SLAPrintObject::SliceIndex::const_iterator it_key = index.lower_bound(key - float(EPSILON));
|
||||
if (it_key == index.end() || it_key->first > key + EPSILON)
|
||||
continue;
|
||||
record = &it_key->second;
|
||||
}
|
||||
|
||||
if (record->model_slices_idx != SLAPrintObject::SliceRecord::NONE)
|
||||
append(model_polygons, get_all_polygons(po->get_model_slices()[record->model_slices_idx], po->instances()));
|
||||
|
||||
if (record->support_slices_idx != SLAPrintObject::SliceRecord::NONE)
|
||||
append(supports_polygons, get_all_polygons(po->get_support_slices()[record->support_slices_idx], po->instances()));
|
||||
}
|
||||
|
||||
model_polygons = union_(model_polygons);
|
||||
double layer_model_area = 0;
|
||||
for (const Polygon& polygon : model_polygons)
|
||||
layer_model_area += polygon.area();
|
||||
|
||||
if (layer_model_area != 0)
|
||||
models_volume += layer_model_area * l_height;
|
||||
|
||||
if (!supports_polygons.empty() && !model_polygons.empty())
|
||||
supports_polygons = diff(supports_polygons, model_polygons);
|
||||
double layer_support_area = 0;
|
||||
for (const Polygon& polygon : supports_polygons)
|
||||
layer_support_area += polygon.area();
|
||||
|
||||
if (layer_support_area != 0)
|
||||
supports_volume += layer_support_area * l_height;
|
||||
|
||||
// Calculation of the slow and fast layers to the future controlling those values on FW
|
||||
|
||||
const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill;
|
||||
const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt;
|
||||
if (is_fast_layer)
|
||||
fast_layers++;
|
||||
else
|
||||
slow_layers++;
|
||||
|
||||
|
||||
// Calculation of the printing time
|
||||
|
||||
if (sliced_layer_cnt < 3)
|
||||
estim_time += init_exp_time;
|
||||
else if (fade_layer_time > exp_time)
|
||||
{
|
||||
fade_layer_time -= delta_fade_time;
|
||||
estim_time += fade_layer_time;
|
||||
}
|
||||
else
|
||||
estim_time += exp_time;
|
||||
|
||||
estim_time += tilt_time;
|
||||
|
||||
sliced_layer_cnt++;
|
||||
}
|
||||
|
||||
m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR;
|
||||
m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR;
|
||||
|
||||
// Estimated printing time
|
||||
// A layers count o the highest object
|
||||
if (max_layers_cnt == 0)
|
||||
m_print_statistics.estimated_print_time = "N/A";
|
||||
else
|
||||
m_print_statistics.estimated_print_time = get_time_dhms(float(estim_time));
|
||||
|
||||
m_print_statistics.fast_layers_count = fast_layers;
|
||||
m_print_statistics.slow_layers_count = slow_layers;
|
||||
}
|
||||
|
||||
// Returns true if an object step is done on all objects and there's at least one object.
|
||||
bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
|
||||
{
|
||||
|
|
@ -1034,7 +1317,7 @@ bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
|
|||
return false;
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
for (const SLAPrintObject *object : m_objects)
|
||||
if (! object->m_state.is_done_unguarded(step))
|
||||
if (! object->is_step_done_unguarded(step))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1060,9 +1343,13 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
|
|||
std::vector<SLAPrintObjectStep> steps;
|
||||
bool invalidated = false;
|
||||
for (const t_config_option_key &opt_key : opt_keys) {
|
||||
if (opt_key == "layer_height") {
|
||||
if ( opt_key == "layer_height"
|
||||
|| opt_key == "faded_layers") {
|
||||
steps.emplace_back(slaposObjectSlice);
|
||||
} else if (opt_key == "supports_enable") {
|
||||
} else if (
|
||||
opt_key == "supports_enable"
|
||||
|| opt_key == "support_points_density_relative"
|
||||
|| opt_key == "support_points_minimal_distance") {
|
||||
steps.emplace_back(slaposSupportPoints);
|
||||
} else if (
|
||||
opt_key == "support_head_front_diameter"
|
||||
|
|
@ -1082,6 +1369,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
|
|||
|| opt_key == "pad_wall_thickness"
|
||||
|| opt_key == "pad_wall_height"
|
||||
|| opt_key == "pad_max_merge_distance"
|
||||
|| opt_key == "pad_wall_tilt"
|
||||
|| opt_key == "pad_edge_radius") {
|
||||
steps.emplace_back(slaposBasePool);
|
||||
} else {
|
||||
|
|
@ -1165,7 +1453,7 @@ const std::vector<ExPolygons> EMPTY_SLICES;
|
|||
const TriangleMesh EMPTY_MESH;
|
||||
}
|
||||
|
||||
const Eigen::MatrixXd& SLAPrintObject::get_support_points() const
|
||||
const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const
|
||||
{
|
||||
return m_supportdata->support_points;
|
||||
}
|
||||
|
|
@ -1244,17 +1532,60 @@ const TriangleMesh &SLAPrintObject::transformed_mesh() const {
|
|||
return m_transformed_rmesh.get();
|
||||
}
|
||||
|
||||
std::vector<Vec3d> SLAPrintObject::transformed_support_points() const
|
||||
std::vector<sla::SupportPoint> SLAPrintObject::transformed_support_points() const
|
||||
{
|
||||
assert(m_model_object != nullptr);
|
||||
auto& spts = m_model_object->sla_support_points;
|
||||
std::vector<sla::SupportPoint>& spts = m_model_object->sla_support_points;
|
||||
|
||||
// this could be cached as well
|
||||
std::vector<Vec3d> ret; ret.reserve(spts.size());
|
||||
std::vector<sla::SupportPoint> ret;
|
||||
ret.reserve(spts.size());
|
||||
|
||||
for(auto& sp : spts) ret.emplace_back( trafo() * Vec3d(sp.cast<double>()));
|
||||
for(sla::SupportPoint& sp : spts) {
|
||||
Vec3d transformed_pos = trafo() * Vec3d(sp.pos(0), sp.pos(1), sp.pos(2));
|
||||
ret.emplace_back(transformed_pos(0), transformed_pos(1), transformed_pos(2), sp.head_front_radius, sp.is_new_island);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DynamicConfig SLAPrintStatistics::config() const
|
||||
{
|
||||
DynamicConfig config;
|
||||
const std::string print_time = Slic3r::short_time(this->estimated_print_time);
|
||||
config.set_key_value("print_time", new ConfigOptionString(print_time));
|
||||
config.set_key_value("objects_used_material", new ConfigOptionFloat(this->objects_used_material));
|
||||
config.set_key_value("support_used_material", new ConfigOptionFloat(this->support_used_material));
|
||||
config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost));
|
||||
config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight));
|
||||
return config;
|
||||
}
|
||||
|
||||
DynamicConfig SLAPrintStatistics::placeholders()
|
||||
{
|
||||
DynamicConfig config;
|
||||
for (const std::string &key : {
|
||||
"print_time", "total_cost", "total_weight",
|
||||
"objects_used_material", "support_used_material" })
|
||||
config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
|
||||
return config;
|
||||
}
|
||||
|
||||
std::string SLAPrintStatistics::finalize_output_path(const std::string &path_in) const
|
||||
{
|
||||
std::string final_path;
|
||||
try {
|
||||
boost::filesystem::path path(path_in);
|
||||
DynamicConfig cfg = this->config();
|
||||
PlaceholderParser pp;
|
||||
std::string new_stem = pp.process(path.stem().string(), 0, &cfg);
|
||||
final_path = (path.parent_path() / (new_stem + path.extension().string())).string();
|
||||
}
|
||||
catch (const std::exception &ex) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to apply the print statistics to the export file name: " << ex.what();
|
||||
final_path = path_in;
|
||||
}
|
||||
return final_path;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
#define slic3r_SLAPrint_hpp_
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "PrintBase.hpp"
|
||||
#include "PrintExport.hpp"
|
||||
#include "Point.hpp"
|
||||
|
|
@ -70,7 +69,7 @@ public:
|
|||
// This will return the transformed mesh which is cached
|
||||
const TriangleMesh& transformed_mesh() const;
|
||||
|
||||
std::vector<Vec3d> transformed_support_points() const;
|
||||
std::vector<sla::SupportPoint> transformed_support_points() const;
|
||||
|
||||
// Get the needed Z elevation for the model geometry if supports should be
|
||||
// displayed. This Z offset should also be applied to the support
|
||||
|
|
@ -91,7 +90,7 @@ public:
|
|||
const std::vector<ExPolygons>& get_support_slices() const;
|
||||
|
||||
// This method returns the support points of this SLAPrintObject.
|
||||
const Eigen::MatrixXd& get_support_points() const;
|
||||
const std::vector<sla::SupportPoint>& get_support_points() const;
|
||||
|
||||
// An index record referencing the slices
|
||||
// (get_model_slices(), get_support_slices()) where the keys are the height
|
||||
|
|
@ -139,6 +138,10 @@ protected:
|
|||
// Invalidate steps based on a set of parameters changed.
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
|
||||
// Which steps have to be performed. Implicitly: all
|
||||
// to be accessible from SLAPrint
|
||||
std::vector<bool> m_stepmask;
|
||||
|
||||
private:
|
||||
// Object specific configuration, pulled from the configuration layer.
|
||||
SLAPrintObjectConfig m_config;
|
||||
|
|
@ -146,9 +149,6 @@ private:
|
|||
Transform3d m_trafo = Transform3d::Identity();
|
||||
std::vector<Instance> m_instances;
|
||||
|
||||
// Which steps have to be performed. Implicitly: all
|
||||
std::vector<bool> m_stepmask;
|
||||
|
||||
// Individual 2d slice polygons from lower z to higher z levels
|
||||
std::vector<ExPolygons> m_model_slices;
|
||||
|
||||
|
|
@ -171,6 +171,35 @@ using PrintObjects = std::vector<SLAPrintObject*>;
|
|||
|
||||
class TriangleMesh;
|
||||
|
||||
struct SLAPrintStatistics
|
||||
{
|
||||
SLAPrintStatistics() { clear(); }
|
||||
std::string estimated_print_time;
|
||||
double objects_used_material;
|
||||
double support_used_material;
|
||||
size_t slow_layers_count;
|
||||
size_t fast_layers_count;
|
||||
double total_cost;
|
||||
double total_weight;
|
||||
|
||||
// Config with the filled in print statistics.
|
||||
DynamicConfig config() const;
|
||||
// Config with the statistics keys populated with placeholder strings.
|
||||
static DynamicConfig placeholders();
|
||||
// Replace the print statistics placeholders in the path.
|
||||
std::string finalize_output_path(const std::string &path_in) const;
|
||||
|
||||
void clear() {
|
||||
estimated_print_time.clear();
|
||||
objects_used_material = 0.;
|
||||
support_used_material = 0.;
|
||||
slow_layers_count = 0;
|
||||
fast_layers_count = 0;
|
||||
total_cost = 0.;
|
||||
total_weight = 0.;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This class is the high level FSM for the SLA printing process.
|
||||
*
|
||||
|
|
@ -194,7 +223,9 @@ public:
|
|||
void clear() override;
|
||||
bool empty() const override { return m_objects.empty(); }
|
||||
ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override;
|
||||
void set_task(const TaskParams ¶ms) override;
|
||||
void process() override;
|
||||
void finalize() override;
|
||||
// Returns true if an object step is done on all objects and there's at least one object.
|
||||
bool is_step_done(SLAPrintObjectStep step) const;
|
||||
// Returns true if the last step was finished with success.
|
||||
|
|
@ -205,8 +236,9 @@ public:
|
|||
}
|
||||
const PrintObjects& objects() const { return m_objects; }
|
||||
|
||||
std::string output_filename() const override
|
||||
{ return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "zip"); }
|
||||
std::string output_filename() const override;
|
||||
|
||||
const SLAPrintStatistics& print_statistics() const { return m_print_statistics; }
|
||||
|
||||
private:
|
||||
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
|
||||
|
|
@ -215,6 +247,8 @@ private:
|
|||
// Invalidate steps based on a set of parameters changed.
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
|
||||
void fill_statistics();
|
||||
|
||||
SLAPrintConfig m_print_config;
|
||||
SLAPrinterConfig m_printer_config;
|
||||
SLAMaterialConfig m_material_config;
|
||||
|
|
@ -246,6 +280,9 @@ private:
|
|||
// The printer itself
|
||||
SLAPrinterPtr m_printer;
|
||||
|
||||
// Estimated print time, material consumed.
|
||||
SLAPrintStatistics m_print_statistics;
|
||||
|
||||
friend SLAPrintObject;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,12 @@ Surface::is_internal() const
|
|||
|| this->surface_type == stInternalVoid;
|
||||
}
|
||||
|
||||
bool
|
||||
Surface::is_top() const
|
||||
{
|
||||
return this->surface_type == stTop;
|
||||
}
|
||||
|
||||
bool
|
||||
Surface::is_bottom() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ public:
|
|||
bool is_solid() const;
|
||||
bool is_external() const;
|
||||
bool is_internal() const;
|
||||
bool is_top() const;
|
||||
bool is_bottom() const;
|
||||
bool is_bridge() const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -23,21 +23,10 @@
|
|||
// Scene's GUI made using imgui library
|
||||
#define ENABLE_IMGUI (1 && ENABLE_1_42_0_ALPHA1)
|
||||
#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI)
|
||||
// Modified Sla support gizmo
|
||||
#define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0_ALPHA1)
|
||||
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
||||
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1)
|
||||
|
||||
|
||||
//====================
|
||||
// 1.42.0.alpha2 techs
|
||||
//====================
|
||||
#define ENABLE_1_42_0_ALPHA2 1
|
||||
|
||||
// Adds print bed models to 3D scene
|
||||
#define ENABLE_PRINT_BED_MODELS (1 && ENABLE_1_42_0_ALPHA2)
|
||||
|
||||
|
||||
//====================
|
||||
// 1.42.0.alpha4 techs
|
||||
//====================
|
||||
|
|
@ -49,10 +38,6 @@
|
|||
#define ENABLE_MOVE_MIN_THRESHOLD (1 && ENABLE_1_42_0_ALPHA4)
|
||||
// Modified initial default placement of generic subparts
|
||||
#define ENABLE_GENERIC_SUBPARTS_PLACEMENT (1 && ENABLE_1_42_0_ALPHA4)
|
||||
// Reworked management of bed shape changes
|
||||
#define ENABLE_REWORKED_BED_SHAPE_CHANGE (1 && ENABLE_1_42_0_ALPHA4)
|
||||
// Use anisotropic filtering on bed plate texture
|
||||
#define ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES (1 && ENABLE_1_42_0_ALPHA4)
|
||||
// Bunch of fixes related to volumes centering
|
||||
#define ENABLE_VOLUMES_CENTERING_FIXES (1 && ENABLE_1_42_0_ALPHA4)
|
||||
|
||||
|
|
@ -65,4 +50,13 @@
|
|||
// Toolbar items hidden/shown in dependence of the user mode
|
||||
#define ENABLE_MODE_AWARE_TOOLBAR_ITEMS (1 && ENABLE_1_42_0_ALPHA5)
|
||||
|
||||
|
||||
//====================
|
||||
// 1.42.0.alpha7 techs
|
||||
//====================
|
||||
#define ENABLE_1_42_0_ALPHA7 1
|
||||
|
||||
// Printbed textures generated from svg files
|
||||
#define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7)
|
||||
|
||||
#endif // _technologies_h_
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public:
|
|||
gluDeleteTess(m_tesselator);
|
||||
}
|
||||
|
||||
Pointf3s tesselate(const ExPolygon &expoly, double z_, bool flipped_)
|
||||
std::vector<Vec3d> tesselate3d(const ExPolygon &expoly, double z_, bool flipped_)
|
||||
{
|
||||
m_z = z_;
|
||||
m_flipped = flipped_;
|
||||
|
|
@ -56,7 +56,7 @@ public:
|
|||
return std::move(m_output_triangles);
|
||||
}
|
||||
|
||||
Pointf3s tesselate(const ExPolygons &expolygons, double z_, bool flipped_)
|
||||
std::vector<Vec3d> tesselate3d(const ExPolygons &expolygons, double z_, bool flipped_)
|
||||
{
|
||||
m_z = z_;
|
||||
m_flipped = flipped_;
|
||||
|
|
@ -189,16 +189,60 @@ private:
|
|||
bool m_flipped;
|
||||
};
|
||||
|
||||
Pointf3s triangulate_expolygons_3df(const ExPolygon &poly, coordf_t z, bool flip)
|
||||
std::vector<Vec3d> triangulate_expolygon_3d(const ExPolygon &poly, coordf_t z, bool flip)
|
||||
{
|
||||
GluTessWrapper tess;
|
||||
return tess.tesselate(poly, z, flip);
|
||||
return tess.tesselate3d(poly, z, flip);
|
||||
}
|
||||
|
||||
Pointf3s triangulate_expolygons_3df(const ExPolygons &polys, coordf_t z, bool flip)
|
||||
std::vector<Vec3d> triangulate_expolygons_3d(const ExPolygons &polys, coordf_t z, bool flip)
|
||||
{
|
||||
GluTessWrapper tess;
|
||||
return tess.tesselate(polys, z, flip);
|
||||
return tess.tesselate3d(polys, z, flip);
|
||||
}
|
||||
|
||||
std::vector<Vec2d> triangulate_expolygon_2d(const ExPolygon &poly, bool flip)
|
||||
{
|
||||
GluTessWrapper tess;
|
||||
std::vector<Vec3d> triangles = tess.tesselate3d(poly, 0, flip);
|
||||
std::vector<Vec2d> out;
|
||||
out.reserve(triangles.size());
|
||||
for (const Vec3d &pt : triangles)
|
||||
out.emplace_back(pt.x(), pt.y());
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<Vec2d> triangulate_expolygons_2d(const ExPolygons &polys, bool flip)
|
||||
{
|
||||
GluTessWrapper tess;
|
||||
std::vector<Vec3d> triangles = tess.tesselate3d(polys, 0, flip);
|
||||
std::vector<Vec2d> out;
|
||||
out.reserve(triangles.size());
|
||||
for (const Vec3d &pt : triangles)
|
||||
out.emplace_back(pt.x(), pt.y());
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<Vec2f> triangulate_expolygon_2f(const ExPolygon &poly, bool flip)
|
||||
{
|
||||
GluTessWrapper tess;
|
||||
std::vector<Vec3d> triangles = tess.tesselate3d(poly, 0, flip);
|
||||
std::vector<Vec2f> out;
|
||||
out.reserve(triangles.size());
|
||||
for (const Vec3d &pt : triangles)
|
||||
out.emplace_back(float(pt.x()), float(pt.y()));
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<Vec2f> triangulate_expolygons_2f(const ExPolygons &polys, bool flip)
|
||||
{
|
||||
GluTessWrapper tess;
|
||||
std::vector<Vec3d> triangles = tess.tesselate3d(polys, 0, flip);
|
||||
std::vector<Vec2f> out;
|
||||
out.reserve(triangles.size());
|
||||
for (const Vec3d &pt : triangles)
|
||||
out.emplace_back(float(pt.x()), float(pt.y()));
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -10,8 +10,12 @@ namespace Slic3r {
|
|||
class ExPolygon;
|
||||
typedef std::vector<ExPolygon> ExPolygons;
|
||||
|
||||
extern Pointf3s triangulate_expolygons_3df(const ExPolygon &poly, coordf_t z = 0, bool flip = false);
|
||||
extern Pointf3s triangulate_expolygons_3df(const ExPolygons &polys, coordf_t z = 0, bool flip = false);
|
||||
extern std::vector<Vec3d> triangulate_expolygon_3d (const ExPolygon &poly, coordf_t z = 0, bool flip = false);
|
||||
extern std::vector<Vec3d> triangulate_expolygons_3d(const ExPolygons &polys, coordf_t z = 0, bool flip = false);
|
||||
extern std::vector<Vec2d> triangulate_expolygon_2d (const ExPolygon &poly, bool flip = false);
|
||||
extern std::vector<Vec2d> triangulate_expolygons_2d(const ExPolygons &polys, bool flip = false);
|
||||
extern std::vector<Vec2f> triangulate_expolygon_2f (const ExPolygon &poly, bool flip = false);
|
||||
extern std::vector<Vec2f> triangulate_expolygons_2f(const ExPolygons &polys, bool flip = false);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
|||
|
|
@ -1781,7 +1781,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
|
|||
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating upper part";
|
||||
ExPolygons section;
|
||||
this->make_expolygons_simple(upper_lines, §ion);
|
||||
Pointf3s triangles = triangulate_expolygons_3df(section, z, true);
|
||||
Pointf3s triangles = triangulate_expolygons_3d(section, z, true);
|
||||
stl_facet facet;
|
||||
facet.normal = stl_normal(0, 0, -1.f);
|
||||
for (size_t i = 0; i < triangles.size(); ) {
|
||||
|
|
@ -1795,7 +1795,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
|
|||
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - triangulating lower part";
|
||||
ExPolygons section;
|
||||
this->make_expolygons_simple(lower_lines, §ion);
|
||||
Pointf3s triangles = triangulate_expolygons_3df(section, z, false);
|
||||
Pointf3s triangles = triangulate_expolygons_3d(section, z, false);
|
||||
stl_facet facet;
|
||||
facet.normal = stl_normal(0, 0, -1.f);
|
||||
for (size_t i = 0; i < triangles.size(); ) {
|
||||
|
|
|
|||
|
|
@ -206,6 +206,69 @@ public:
|
|||
void reset() { closure = Closure(); }
|
||||
};
|
||||
|
||||
// Shorten the dhms time by removing the seconds, rounding the dhm to full minutes
|
||||
// and removing spaces.
|
||||
static std::string short_time(const std::string &time)
|
||||
{
|
||||
// Parse the dhms time format.
|
||||
int days = 0;
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
int seconds = 0;
|
||||
if (time.find('d') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dd %dh %dm %ds", &days, &hours, &minutes, &seconds);
|
||||
else if (time.find('h') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dh %dm %ds", &hours, &minutes, &seconds);
|
||||
else if (time.find('m') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%dm %ds", &minutes, &seconds);
|
||||
else if (time.find('s') != std::string::npos)
|
||||
::sscanf(time.c_str(), "%ds", &seconds);
|
||||
// Round to full minutes.
|
||||
if (days + hours + minutes > 0 && seconds >= 30) {
|
||||
if (++minutes == 60) {
|
||||
minutes = 0;
|
||||
if (++hours == 24) {
|
||||
hours = 0;
|
||||
++days;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Format the dhm time.
|
||||
char buffer[64];
|
||||
if (days > 0)
|
||||
::sprintf(buffer, "%dd%dh%dm", days, hours, minutes);
|
||||
else if (hours > 0)
|
||||
::sprintf(buffer, "%dh%dm", hours, minutes);
|
||||
else if (minutes > 0)
|
||||
::sprintf(buffer, "%dm", minutes);
|
||||
else
|
||||
::sprintf(buffer, "%ds", seconds);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Returns the given time is seconds in format DDd HHh MMm SSs
|
||||
static std::string get_time_dhms(float time_in_secs)
|
||||
{
|
||||
int days = (int)(time_in_secs / 86400.0f);
|
||||
time_in_secs -= (float)days * 86400.0f;
|
||||
int hours = (int)(time_in_secs / 3600.0f);
|
||||
time_in_secs -= (float)hours * 3600.0f;
|
||||
int minutes = (int)(time_in_secs / 60.0f);
|
||||
time_in_secs -= (float)minutes * 60.0f;
|
||||
|
||||
char buffer[64];
|
||||
if (days > 0)
|
||||
::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs);
|
||||
else if (hours > 0)
|
||||
::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs);
|
||||
else if (minutes > 0)
|
||||
::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs);
|
||||
else
|
||||
::sprintf(buffer, "%ds", (int)time_in_secs);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#if WIN32
|
||||
|
|
|
|||
|
|
@ -42,22 +42,27 @@ namespace Slic3r {
|
|||
|
||||
static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error;
|
||||
|
||||
void set_logging_level(unsigned int level)
|
||||
static boost::log::trivial::severity_level level_to_boost(unsigned level)
|
||||
{
|
||||
switch (level) {
|
||||
// Report fatal errors only.
|
||||
case 0: logSeverity = boost::log::trivial::fatal; break;
|
||||
case 0: return boost::log::trivial::fatal;
|
||||
// Report fatal errors and errors.
|
||||
case 1: logSeverity = boost::log::trivial::error; break;
|
||||
case 1: return boost::log::trivial::error;
|
||||
// Report fatal errors, errors and warnings.
|
||||
case 2: logSeverity = boost::log::trivial::warning; break;
|
||||
case 2: return boost::log::trivial::warning;
|
||||
// Report all errors, warnings and infos.
|
||||
case 3: logSeverity = boost::log::trivial::info; break;
|
||||
case 3: return boost::log::trivial::info;
|
||||
// Report all errors, warnings, infos and debugging.
|
||||
case 4: logSeverity = boost::log::trivial::debug; break;
|
||||
case 4: return boost::log::trivial::debug;
|
||||
// Report everyting including fine level tracing information.
|
||||
default: logSeverity = boost::log::trivial::trace; break;
|
||||
default: return boost::log::trivial::trace;
|
||||
}
|
||||
}
|
||||
|
||||
void set_logging_level(unsigned int level)
|
||||
{
|
||||
logSeverity = level_to_boost(level);
|
||||
|
||||
boost::log::core::get()->set_filter
|
||||
(
|
||||
|
|
@ -73,6 +78,7 @@ unsigned get_logging_level()
|
|||
case boost::log::trivial::warning : return 2;
|
||||
case boost::log::trivial::info : return 3;
|
||||
case boost::log::trivial::debug : return 4;
|
||||
case boost::log::trivial::trace : return 5;
|
||||
default: return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -88,21 +94,7 @@ static struct RunOnInit {
|
|||
|
||||
void trace(unsigned int level, const char *message)
|
||||
{
|
||||
boost::log::trivial::severity_level severity = boost::log::trivial::trace;
|
||||
switch (level) {
|
||||
// Report fatal errors only.
|
||||
case 0: severity = boost::log::trivial::fatal; break;
|
||||
// Report fatal errors and errors.
|
||||
case 1: severity = boost::log::trivial::error; break;
|
||||
// Report fatal errors, errors and warnings.
|
||||
case 2: severity = boost::log::trivial::warning; break;
|
||||
// Report all errors, warnings and infos.
|
||||
case 3: severity = boost::log::trivial::info; break;
|
||||
// Report all errors, warnings, infos and debugging.
|
||||
case 4: severity = boost::log::trivial::debug; break;
|
||||
// Report everyting including fine level tracing information.
|
||||
default: severity = boost::log::trivial::trace; break;
|
||||
}
|
||||
boost::log::trivial::severity_level severity = level_to_boost(level);
|
||||
|
||||
BOOST_LOG_STREAM_WITH_PARAMS(::boost::log::trivial::logger::get(),\
|
||||
(::boost::log::keywords::severity = severity)) << message;
|
||||
|
|
|
|||
2975
src/nanosvg/nanosvg.h
Normal file
2975
src/nanosvg/nanosvg.h
Normal file
File diff suppressed because it is too large
Load diff
1452
src/nanosvg/nanosvgrast.h
Normal file
1452
src/nanosvg/nanosvgrast.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -74,6 +74,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/BedShapeDialog.hpp
|
||||
GUI/2DBed.cpp
|
||||
GUI/2DBed.hpp
|
||||
GUI/3DBed.cpp
|
||||
GUI/3DBed.hpp
|
||||
GUI/wxExtensions.cpp
|
||||
GUI/wxExtensions.hpp
|
||||
GUI/WipeTowerDialog.cpp
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "2DBed.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#include <wx/dcbuffer.h>
|
||||
|
||||
|
|
@ -9,6 +10,19 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
Bed_2D::Bed_2D(wxWindow* parent) :
|
||||
wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -1), wxTAB_TRAVERSAL)
|
||||
{
|
||||
SetBackgroundStyle(wxBG_STYLE_PAINT); // to avoid assert message after wxAutoBufferedPaintDC
|
||||
#ifdef __APPLE__
|
||||
m_user_drawn_background = false;
|
||||
#endif /*__APPLE__*/
|
||||
Bind(wxEVT_PAINT, ([this](wxPaintEvent e) { repaint(); }));
|
||||
Bind(wxEVT_LEFT_DOWN, ([this](wxMouseEvent event) { mouse_event(event); }));
|
||||
Bind(wxEVT_MOTION, ([this](wxMouseEvent event) { mouse_event(event); }));
|
||||
Bind(wxEVT_SIZE, ([this](wxSizeEvent e) { Refresh(); }));
|
||||
}
|
||||
void Bed_2D::repaint()
|
||||
{
|
||||
wxAutoBufferedPaintDC dc(this);
|
||||
|
|
|
|||
|
|
@ -25,21 +25,7 @@ class Bed_2D : public wxPanel
|
|||
void set_pos(Vec2d pos);
|
||||
|
||||
public:
|
||||
Bed_2D(wxWindow* parent)
|
||||
{
|
||||
Create(parent, wxID_ANY, wxDefaultPosition, wxSize(250, -1), wxTAB_TRAVERSAL);
|
||||
SetBackgroundStyle(wxBG_STYLE_PAINT); // to avoid assert message after wxAutoBufferedPaintDC
|
||||
// m_user_drawn_background = $^O ne 'darwin';
|
||||
#ifdef __APPLE__
|
||||
m_user_drawn_background = false;
|
||||
#endif /*__APPLE__*/
|
||||
Bind(wxEVT_PAINT, ([this](wxPaintEvent e) { repaint(); }));
|
||||
// EVT_ERASE_BACKGROUND($self, sub{}) if $self->{user_drawn_background};
|
||||
// Bind(EVT_MOUSE_EVENTS, ([this](wxMouseEvent event) {/*mouse_event()*/; }));
|
||||
Bind(wxEVT_LEFT_DOWN, ([this](wxMouseEvent event) { mouse_event(event); }));
|
||||
Bind(wxEVT_MOTION, ([this](wxMouseEvent event) { mouse_event(event); }));
|
||||
Bind(wxEVT_SIZE, ([this](wxSizeEvent e) { Refresh(); }));
|
||||
}
|
||||
Bed_2D(wxWindow* parent);
|
||||
~Bed_2D() {}
|
||||
|
||||
std::vector<Vec2d> m_bed_shape;
|
||||
|
|
|
|||
796
src/slic3r/GUI/3DBed.cpp
Normal file
796
src/slic3r/GUI/3DBed.cpp
Normal file
|
|
@ -0,0 +1,796 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include "3DBed.hpp"
|
||||
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "PresetBundle.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
static const float GROUND_Z = -0.02f;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords)
|
||||
{
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
m_vertices.clear();
|
||||
unsigned int v_size = 3 * (unsigned int)triangles.size();
|
||||
|
||||
if (v_size == 0)
|
||||
return false;
|
||||
|
||||
m_vertices = std::vector<Vertex>(v_size, Vertex());
|
||||
|
||||
float min_x = unscale<float>(triangles[0].points[0](0));
|
||||
float min_y = unscale<float>(triangles[0].points[0](1));
|
||||
float max_x = min_x;
|
||||
float max_y = min_y;
|
||||
|
||||
unsigned int v_count = 0;
|
||||
for (const Polygon& t : triangles)
|
||||
{
|
||||
for (unsigned int i = 0; i < 3; ++i)
|
||||
{
|
||||
Vertex& v = m_vertices[v_count];
|
||||
|
||||
const Point& p = t.points[i];
|
||||
float x = unscale<float>(p(0));
|
||||
float y = unscale<float>(p(1));
|
||||
|
||||
v.position[0] = x;
|
||||
v.position[1] = y;
|
||||
v.position[2] = z;
|
||||
|
||||
if (generate_tex_coords)
|
||||
{
|
||||
v.tex_coords[0] = x;
|
||||
v.tex_coords[1] = y;
|
||||
|
||||
min_x = std::min(min_x, x);
|
||||
max_x = std::max(max_x, x);
|
||||
min_y = std::min(min_y, y);
|
||||
max_y = std::max(max_y, y);
|
||||
}
|
||||
|
||||
++v_count;
|
||||
}
|
||||
}
|
||||
|
||||
if (generate_tex_coords)
|
||||
{
|
||||
float size_x = max_x - min_x;
|
||||
float size_y = max_y - min_y;
|
||||
|
||||
if ((size_x != 0.0f) && (size_y != 0.0f))
|
||||
{
|
||||
float inv_size_x = 1.0f / size_x;
|
||||
float inv_size_y = -1.0f / size_y;
|
||||
for (Vertex& v : m_vertices)
|
||||
{
|
||||
v.tex_coords[0] = (v.tex_coords[0] - min_x) * inv_size_x;
|
||||
v.tex_coords[1] = (v.tex_coords[1] - min_y) * inv_size_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
m_vertices.clear();
|
||||
m_tex_coords.clear();
|
||||
|
||||
unsigned int v_size = 9 * (unsigned int)triangles.size();
|
||||
unsigned int t_size = 6 * (unsigned int)triangles.size();
|
||||
if (v_size == 0)
|
||||
return false;
|
||||
|
||||
m_vertices = std::vector<float>(v_size, 0.0f);
|
||||
if (generate_tex_coords)
|
||||
m_tex_coords = std::vector<float>(t_size, 0.0f);
|
||||
|
||||
float min_x = unscale<float>(triangles[0].points[0](0));
|
||||
float min_y = unscale<float>(triangles[0].points[0](1));
|
||||
float max_x = min_x;
|
||||
float max_y = min_y;
|
||||
|
||||
unsigned int v_coord = 0;
|
||||
unsigned int t_coord = 0;
|
||||
for (const Polygon& t : triangles)
|
||||
{
|
||||
for (unsigned int v = 0; v < 3; ++v)
|
||||
{
|
||||
const Point& p = t.points[v];
|
||||
float x = unscale<float>(p(0));
|
||||
float y = unscale<float>(p(1));
|
||||
|
||||
m_vertices[v_coord++] = x;
|
||||
m_vertices[v_coord++] = y;
|
||||
m_vertices[v_coord++] = z;
|
||||
|
||||
if (generate_tex_coords)
|
||||
{
|
||||
m_tex_coords[t_coord++] = x;
|
||||
m_tex_coords[t_coord++] = y;
|
||||
|
||||
min_x = std::min(min_x, x);
|
||||
max_x = std::max(max_x, x);
|
||||
min_y = std::min(min_y, y);
|
||||
max_y = std::max(max_y, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (generate_tex_coords)
|
||||
{
|
||||
float size_x = max_x - min_x;
|
||||
float size_y = max_y - min_y;
|
||||
|
||||
if ((size_x != 0.0f) && (size_y != 0.0f))
|
||||
{
|
||||
float inv_size_x = 1.0f / size_x;
|
||||
float inv_size_y = -1.0f / size_y;
|
||||
for (unsigned int i = 0; i < m_tex_coords.size(); i += 2)
|
||||
{
|
||||
m_tex_coords[i] = (m_tex_coords[i] - min_x) * inv_size_x;
|
||||
m_tex_coords[i + 1] = (m_tex_coords[i + 1] - min_y) * inv_size_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GeometryBuffer::set_from_lines(const Lines& lines, float z)
|
||||
{
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
m_vertices.clear();
|
||||
|
||||
unsigned int v_size = 2 * (unsigned int)lines.size();
|
||||
if (v_size == 0)
|
||||
return false;
|
||||
|
||||
m_vertices = std::vector<Vertex>(v_size, Vertex());
|
||||
|
||||
unsigned int v_count = 0;
|
||||
for (const Line& l : lines)
|
||||
{
|
||||
Vertex& v1 = m_vertices[v_count];
|
||||
v1.position[0] = unscale<float>(l.a(0));
|
||||
v1.position[1] = unscale<float>(l.a(1));
|
||||
v1.position[2] = z;
|
||||
++v_count;
|
||||
|
||||
Vertex& v2 = m_vertices[v_count];
|
||||
v2.position[0] = unscale<float>(l.b(0));
|
||||
v2.position[1] = unscale<float>(l.b(1));
|
||||
v2.position[2] = z;
|
||||
++v_count;
|
||||
}
|
||||
#else
|
||||
m_vertices.clear();
|
||||
m_tex_coords.clear();
|
||||
|
||||
unsigned int size = 6 * (unsigned int)lines.size();
|
||||
if (size == 0)
|
||||
return false;
|
||||
|
||||
m_vertices = std::vector<float>(size, 0.0f);
|
||||
|
||||
unsigned int coord = 0;
|
||||
for (const Line& l : lines)
|
||||
{
|
||||
m_vertices[coord++] = unscale<float>(l.a(0));
|
||||
m_vertices[coord++] = unscale<float>(l.a(1));
|
||||
m_vertices[coord++] = z;
|
||||
m_vertices[coord++] = unscale<float>(l.b(0));
|
||||
m_vertices[coord++] = unscale<float>(l.b(1));
|
||||
m_vertices[coord++] = z;
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
const float* GeometryBuffer::get_vertices_data() const
|
||||
{
|
||||
return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr;
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
const double Bed3D::Axes::Radius = 0.5;
|
||||
const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius;
|
||||
const double Bed3D::Axes::ArrowLength = 5.0;
|
||||
|
||||
Bed3D::Axes::Axes()
|
||||
: origin(Vec3d::Zero())
|
||||
, length(Vec3d::Zero())
|
||||
{
|
||||
m_quadric = ::gluNewQuadric();
|
||||
if (m_quadric != nullptr)
|
||||
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
|
||||
}
|
||||
|
||||
Bed3D::Axes::~Axes()
|
||||
{
|
||||
if (m_quadric != nullptr)
|
||||
::gluDeleteQuadric(m_quadric);
|
||||
}
|
||||
|
||||
void Bed3D::Axes::render() const
|
||||
{
|
||||
if (m_quadric == nullptr)
|
||||
return;
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glEnable(GL_LIGHTING));
|
||||
|
||||
// x axis
|
||||
glsafe(::glColor3f(1.0f, 0.0f, 0.0f));
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(origin(0), origin(1), origin(2)));
|
||||
glsafe(::glRotated(90.0, 0.0, 1.0, 0.0));
|
||||
render_axis(length(0));
|
||||
glsafe(::glPopMatrix());
|
||||
|
||||
// y axis
|
||||
glsafe(::glColor3f(0.0f, 1.0f, 0.0f));
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(origin(0), origin(1), origin(2)));
|
||||
glsafe(::glRotated(-90.0, 1.0, 0.0, 0.0));
|
||||
render_axis(length(1));
|
||||
glsafe(::glPopMatrix());
|
||||
|
||||
// z axis
|
||||
glsafe(::glColor3f(0.0f, 0.0f, 1.0f));
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glTranslated(origin(0), origin(1), origin(2)));
|
||||
render_axis(length(2));
|
||||
glsafe(::glPopMatrix());
|
||||
|
||||
glsafe(::glDisable(GL_LIGHTING));
|
||||
}
|
||||
|
||||
void Bed3D::Axes::render_axis(double length) const
|
||||
{
|
||||
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
|
||||
::gluCylinder(m_quadric, Radius, Radius, length, 32, 1);
|
||||
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
|
||||
::gluDisk(m_quadric, 0.0, Radius, 32, 1);
|
||||
glsafe(::glTranslated(0.0, 0.0, length));
|
||||
::gluQuadricOrientation(m_quadric, GLU_OUTSIDE);
|
||||
::gluCylinder(m_quadric, ArrowBaseRadius, 0.0, ArrowLength, 32, 1);
|
||||
::gluQuadricOrientation(m_quadric, GLU_INSIDE);
|
||||
::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1);
|
||||
}
|
||||
|
||||
Bed3D::Bed3D()
|
||||
: m_type(Custom)
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
, m_vbo_id(0)
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
, m_scale_factor(1.0f)
|
||||
{
|
||||
}
|
||||
|
||||
bool Bed3D::set_shape(const Pointfs& shape)
|
||||
{
|
||||
EType new_type = detect_type(shape);
|
||||
if (m_shape == shape && m_type == new_type)
|
||||
// No change, no need to update the UI.
|
||||
return false;
|
||||
|
||||
m_shape = shape;
|
||||
m_type = new_type;
|
||||
|
||||
calc_bounding_box();
|
||||
|
||||
ExPolygon poly;
|
||||
for (const Vec2d& p : m_shape)
|
||||
{
|
||||
poly.contour.append(Point(scale_(p(0)), scale_(p(1))));
|
||||
}
|
||||
|
||||
calc_triangles(poly);
|
||||
|
||||
const BoundingBox& bed_bbox = poly.contour.bounding_box();
|
||||
calc_gridlines(poly, bed_bbox);
|
||||
|
||||
m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour;
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
reset();
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
// Set the origin and size for painting of the coordinate system axes.
|
||||
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
|
||||
m_axes.length = 0.1 * get_bounding_box().max_size() * Vec3d::Ones();
|
||||
|
||||
// Let the calee to update the UI.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Bed3D::contains(const Point& point) const
|
||||
{
|
||||
return m_polygon.contains(point);
|
||||
}
|
||||
|
||||
Point Bed3D::point_projection(const Point& point) const
|
||||
{
|
||||
return m_polygon.point_projection(point);
|
||||
}
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void Bed3D::render(float theta, bool useVBOs, float scale_factor) const
|
||||
{
|
||||
m_scale_factor = scale_factor;
|
||||
|
||||
EType type = useVBOs ? m_type : Custom;
|
||||
switch (type)
|
||||
|
||||
{
|
||||
case MK2:
|
||||
{
|
||||
render_prusa("mk2", theta > 90.0f);
|
||||
break;
|
||||
}
|
||||
case MK3:
|
||||
{
|
||||
render_prusa("mk3", theta > 90.0f);
|
||||
break;
|
||||
}
|
||||
case SL1:
|
||||
{
|
||||
render_prusa("sl1", theta > 90.0f);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case Custom:
|
||||
{
|
||||
render_custom();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void Bed3D::render(float theta, bool useVBOs, float scale_factor) const
|
||||
{
|
||||
m_scale_factor = scale_factor;
|
||||
|
||||
if (m_shape.empty())
|
||||
return;
|
||||
|
||||
switch (m_type)
|
||||
{
|
||||
case MK2:
|
||||
{
|
||||
render_prusa("mk2", theta, useVBOs);
|
||||
break;
|
||||
}
|
||||
case MK3:
|
||||
{
|
||||
render_prusa("mk3", theta, useVBOs);
|
||||
break;
|
||||
}
|
||||
case SL1:
|
||||
{
|
||||
render_prusa("sl1", theta, useVBOs);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case Custom:
|
||||
{
|
||||
render_custom();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
void Bed3D::render_axes() const
|
||||
{
|
||||
if (!m_shape.empty())
|
||||
m_axes.render();
|
||||
}
|
||||
|
||||
void Bed3D::calc_bounding_box()
|
||||
{
|
||||
m_bounding_box = BoundingBoxf3();
|
||||
for (const Vec2d& p : m_shape)
|
||||
{
|
||||
m_bounding_box.merge(Vec3d(p(0), p(1), 0.0));
|
||||
}
|
||||
}
|
||||
|
||||
void Bed3D::calc_triangles(const ExPolygon& poly)
|
||||
{
|
||||
Polygons triangles;
|
||||
poly.triangulate(&triangles);
|
||||
|
||||
if (!m_triangles.set_from_triangles(triangles, GROUND_Z, m_type != Custom))
|
||||
printf("Unable to create bed triangles\n");
|
||||
}
|
||||
|
||||
void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
|
||||
{
|
||||
Polylines axes_lines;
|
||||
for (coord_t x = bed_bbox.min(0); x <= bed_bbox.max(0); x += scale_(10.0))
|
||||
{
|
||||
Polyline line;
|
||||
line.append(Point(x, bed_bbox.min(1)));
|
||||
line.append(Point(x, bed_bbox.max(1)));
|
||||
axes_lines.push_back(line);
|
||||
}
|
||||
for (coord_t y = bed_bbox.min(1); y <= bed_bbox.max(1); y += scale_(10.0))
|
||||
{
|
||||
Polyline line;
|
||||
line.append(Point(bed_bbox.min(0), y));
|
||||
line.append(Point(bed_bbox.max(0), y));
|
||||
axes_lines.push_back(line);
|
||||
}
|
||||
|
||||
// clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped
|
||||
Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, (float)SCALED_EPSILON)));
|
||||
|
||||
// append bed contours
|
||||
Lines contour_lines = to_lines(poly);
|
||||
std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines));
|
||||
|
||||
if (!m_gridlines.set_from_lines(gridlines, GROUND_Z))
|
||||
printf("Unable to create bed grid lines\n");
|
||||
}
|
||||
|
||||
Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const
|
||||
{
|
||||
EType type = Custom;
|
||||
|
||||
auto bundle = wxGetApp().preset_bundle;
|
||||
if (bundle != nullptr)
|
||||
{
|
||||
const Preset* curr = &bundle->printers.get_selected_preset();
|
||||
while (curr != nullptr)
|
||||
{
|
||||
if (curr->config.has("bed_shape"))
|
||||
{
|
||||
if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research") && (shape == dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values))
|
||||
{
|
||||
if (boost::contains(curr->name, "SL1"))
|
||||
{
|
||||
type = SL1;
|
||||
break;
|
||||
}
|
||||
else if (boost::contains(curr->name, "MK3") || boost::contains(curr->name, "MK2.5"))
|
||||
{
|
||||
type = MK3;
|
||||
break;
|
||||
}
|
||||
else if (boost::contains(curr->name, "MK2"))
|
||||
{
|
||||
type = MK2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
curr = bundle->printers.get_preset_parent(*curr);
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void Bed3D::render_prusa(const std::string &key, bool bottom) const
|
||||
{
|
||||
std::string tex_path = resources_dir() + "/icons/bed/" + key;
|
||||
|
||||
std::string model_path = resources_dir() + "/models/" + key;
|
||||
|
||||
// use anisotropic filter if graphic card allows
|
||||
GLfloat max_anisotropy = 0.0f;
|
||||
if (glewIsSupported("GL_EXT_texture_filter_anisotropic"))
|
||||
::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
|
||||
|
||||
// use higher resolution images if graphic card allows
|
||||
GLint max_tex_size;
|
||||
::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
|
||||
|
||||
// clamp or the texture generation becomes too slow
|
||||
max_tex_size = std::min(max_tex_size, 8192);
|
||||
|
||||
std::string filename = tex_path + ".svg";
|
||||
|
||||
if ((m_texture.get_id() == 0) || (m_texture.get_source() != filename))
|
||||
{
|
||||
if (!m_texture.load_from_svg_file(filename, true, max_tex_size))
|
||||
{
|
||||
render_custom();
|
||||
return;
|
||||
}
|
||||
|
||||
if (max_anisotropy > 0.0f)
|
||||
{
|
||||
::glBindTexture(GL_TEXTURE_2D, m_texture.get_id());
|
||||
::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy);
|
||||
::glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!bottom)
|
||||
{
|
||||
filename = model_path + "_bed.stl";
|
||||
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, true)) {
|
||||
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
|
||||
if (key == "mk2")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 7.5, -0.03);
|
||||
else if (key == "mk3")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 5.5, 2.43);
|
||||
else if (key == "sl1")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 0.0, -0.03);
|
||||
|
||||
m_model.center_around(offset);
|
||||
}
|
||||
|
||||
if (!m_model.get_filename().empty())
|
||||
{
|
||||
::glEnable(GL_LIGHTING);
|
||||
m_model.render();
|
||||
::glDisable(GL_LIGHTING);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int triangles_vcount = m_triangles.get_vertices_count();
|
||||
if (triangles_vcount > 0)
|
||||
{
|
||||
if (m_vbo_id == 0)
|
||||
{
|
||||
::glGenBuffers(1, &m_vbo_id);
|
||||
::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id);
|
||||
::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW);
|
||||
::glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_position_offset());
|
||||
::glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_tex_coords_offset());
|
||||
::glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
::glDepthMask(GL_FALSE);
|
||||
|
||||
::glEnable(GL_BLEND);
|
||||
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
::glEnable(GL_TEXTURE_2D);
|
||||
::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
|
||||
if (bottom)
|
||||
::glFrontFace(GL_CW);
|
||||
|
||||
render_prusa_shader(triangles_vcount, bottom);
|
||||
|
||||
if (bottom)
|
||||
::glFrontFace(GL_CCW);
|
||||
|
||||
::glDisable(GL_TEXTURE_2D);
|
||||
|
||||
::glDisable(GL_BLEND);
|
||||
::glDepthMask(GL_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void Bed3D::render_prusa_shader(unsigned int vertices_count, bool transparent) const
|
||||
{
|
||||
if (m_shader.get_shader_program_id() == 0)
|
||||
m_shader.init("printbed.vs", "printbed.fs");
|
||||
|
||||
if (m_shader.is_initialized())
|
||||
{
|
||||
m_shader.start_using();
|
||||
m_shader.set_uniform("transparent_background", transparent);
|
||||
|
||||
::glBindTexture(GL_TEXTURE_2D, (GLuint)m_texture.get_id());
|
||||
::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id);
|
||||
::glEnableVertexAttribArray(0);
|
||||
::glEnableVertexAttribArray(1);
|
||||
::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertices_count);
|
||||
::glDisableVertexAttribArray(1);
|
||||
::glDisableVertexAttribArray(0);
|
||||
::glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
::glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
m_shader.stop_using();
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) const
|
||||
{
|
||||
std::string tex_path = resources_dir() + "/icons/bed/" + key;
|
||||
|
||||
// use higher resolution images if graphic card allows
|
||||
GLint max_tex_size;
|
||||
::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
|
||||
|
||||
// temporary set to lowest resolution
|
||||
max_tex_size = 2048;
|
||||
|
||||
if (max_tex_size >= 8192)
|
||||
tex_path += "_8192";
|
||||
else if (max_tex_size >= 4096)
|
||||
tex_path += "_4096";
|
||||
|
||||
std::string model_path = resources_dir() + "/models/" + key;
|
||||
|
||||
// use anisotropic filter if graphic card allows
|
||||
GLfloat max_anisotropy = 0.0f;
|
||||
if (glewIsSupported("GL_EXT_texture_filter_anisotropic"))
|
||||
::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy);
|
||||
|
||||
std::string filename = tex_path + "_top.png";
|
||||
if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename))
|
||||
{
|
||||
if (!m_top_texture.load_from_file(filename, true))
|
||||
{
|
||||
render_custom();
|
||||
return;
|
||||
}
|
||||
|
||||
if (max_anisotropy > 0.0f)
|
||||
{
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_top_texture.get_id()));
|
||||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
}
|
||||
}
|
||||
|
||||
filename = tex_path + "_bottom.png";
|
||||
if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename))
|
||||
{
|
||||
if (!m_bottom_texture.load_from_file(filename, true))
|
||||
{
|
||||
render_custom();
|
||||
return;
|
||||
}
|
||||
|
||||
if (max_anisotropy > 0.0f)
|
||||
{
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_bottom_texture.get_id()));
|
||||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (theta <= 90.0f)
|
||||
{
|
||||
filename = model_path + "_bed.stl";
|
||||
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs)) {
|
||||
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
|
||||
if (key == "mk2")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 7.5, -0.03);
|
||||
else if (key == "mk3")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 5.5, 2.43);
|
||||
else if (key == "sl1")
|
||||
// hardcoded value to match the stl model
|
||||
offset += Vec3d(0.0, 0.0, -0.03);
|
||||
|
||||
m_model.center_around(offset);
|
||||
}
|
||||
|
||||
if (!m_model.get_filename().empty())
|
||||
{
|
||||
glsafe(::glEnable(GL_LIGHTING));
|
||||
m_model.render();
|
||||
glsafe(::glDisable(GL_LIGHTING));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int triangles_vcount = m_triangles.get_vertices_count();
|
||||
if (triangles_vcount > 0)
|
||||
{
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
glsafe(::glEnable(GL_TEXTURE_2D));
|
||||
glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE));
|
||||
|
||||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY));
|
||||
|
||||
if (theta > 90.0f)
|
||||
glsafe(::glFrontFace(GL_CW));
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id()));
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()));
|
||||
glsafe(::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords()));
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
|
||||
|
||||
if (theta > 90.0f)
|
||||
glsafe(::glFrontFace(GL_CCW));
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY));
|
||||
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
|
||||
|
||||
glsafe(::glDisable(GL_TEXTURE_2D));
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
void Bed3D::render_custom() const
|
||||
{
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
m_texture.reset();
|
||||
#else
|
||||
m_top_texture.reset();
|
||||
m_bottom_texture.reset();
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
unsigned int triangles_vcount = m_triangles.get_vertices_count();
|
||||
if (triangles_vcount > 0)
|
||||
{
|
||||
glsafe(::glEnable(GL_LIGHTING));
|
||||
glsafe(::glDisable(GL_DEPTH_TEST));
|
||||
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
|
||||
glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f));
|
||||
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data());
|
||||
#else
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()));
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
|
||||
|
||||
// draw grid
|
||||
unsigned int gridlines_vcount = m_gridlines.get_vertices_count();
|
||||
|
||||
// we need depth test for grid, otherwise it would disappear when looking the object from below
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glLineWidth(3.0f * m_scale_factor));
|
||||
glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f));
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data());
|
||||
#else
|
||||
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices()));
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount));
|
||||
|
||||
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glDisable(GL_LIGHTING));
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void Bed3D::reset()
|
||||
{
|
||||
if (m_vbo_id > 0)
|
||||
{
|
||||
::glDeleteBuffers(1, &m_vbo_id);
|
||||
m_vbo_id = 0;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
147
src/slic3r/GUI/3DBed.hpp
Normal file
147
src/slic3r/GUI/3DBed.hpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#ifndef slic3r_3DBed_hpp_
|
||||
#define slic3r_3DBed_hpp_
|
||||
|
||||
#include "GLTexture.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
#include "GLShader.hpp"
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
class GLUquadric;
|
||||
typedef class GLUquadric GLUquadricObj;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
class GeometryBuffer
|
||||
{
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
struct Vertex
|
||||
{
|
||||
float position[3];
|
||||
float tex_coords[2];
|
||||
|
||||
Vertex()
|
||||
{
|
||||
position[0] = 0.0f; position[1] = 0.0f; position[2] = 0.0f;
|
||||
tex_coords[0] = 0.0f; tex_coords[1] = 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<Vertex> m_vertices;
|
||||
#else
|
||||
std::vector<float> m_vertices;
|
||||
std::vector<float> m_tex_coords;
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
public:
|
||||
bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords);
|
||||
bool set_from_lines(const Lines& lines, float z);
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
const float* get_vertices_data() const;
|
||||
unsigned int get_vertices_data_size() const { return (unsigned int)m_vertices.size() * get_vertex_data_size(); }
|
||||
unsigned int get_vertex_data_size() const { return (unsigned int)(5 * sizeof(float)); }
|
||||
unsigned int get_position_offset() const { return 0; }
|
||||
unsigned int get_tex_coords_offset() const { return (unsigned int)(3 * sizeof(float)); }
|
||||
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); }
|
||||
#else
|
||||
const float* get_vertices() const { return m_vertices.data(); }
|
||||
const float* get_tex_coords() const { return m_tex_coords.data(); }
|
||||
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size() / 3; }
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
};
|
||||
|
||||
class Bed3D
|
||||
{
|
||||
struct Axes
|
||||
{
|
||||
static const double Radius;
|
||||
static const double ArrowBaseRadius;
|
||||
static const double ArrowLength;
|
||||
Vec3d origin;
|
||||
Vec3d length;
|
||||
GLUquadricObj* m_quadric;
|
||||
|
||||
Axes();
|
||||
~Axes();
|
||||
|
||||
void render() const;
|
||||
|
||||
private:
|
||||
void render_axis(double length) const;
|
||||
};
|
||||
|
||||
public:
|
||||
enum EType : unsigned char
|
||||
{
|
||||
MK2,
|
||||
MK3,
|
||||
SL1,
|
||||
Custom,
|
||||
Num_Types
|
||||
};
|
||||
|
||||
private:
|
||||
EType m_type;
|
||||
Pointfs m_shape;
|
||||
BoundingBoxf3 m_bounding_box;
|
||||
Polygon m_polygon;
|
||||
GeometryBuffer m_triangles;
|
||||
GeometryBuffer m_gridlines;
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
mutable GLTexture m_texture;
|
||||
mutable Shader m_shader;
|
||||
mutable unsigned int m_vbo_id;
|
||||
#else
|
||||
mutable GLTexture m_top_texture;
|
||||
mutable GLTexture m_bottom_texture;
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
mutable GLBed m_model;
|
||||
Axes m_axes;
|
||||
|
||||
mutable float m_scale_factor;
|
||||
|
||||
public:
|
||||
Bed3D();
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
~Bed3D() { reset(); }
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
EType get_type() const { return m_type; }
|
||||
|
||||
bool is_prusa() const { return (m_type == MK2) || (m_type == MK3) || (m_type == SL1); }
|
||||
bool is_custom() const { return m_type == Custom; }
|
||||
|
||||
const Pointfs& get_shape() const { return m_shape; }
|
||||
// Return true if the bed shape changed, so the calee will update the UI.
|
||||
bool set_shape(const Pointfs& shape);
|
||||
|
||||
const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; }
|
||||
bool contains(const Point& point) const;
|
||||
Point point_projection(const Point& point) const;
|
||||
|
||||
void render(float theta, bool useVBOs, float scale_factor) const;
|
||||
void render_axes() const;
|
||||
|
||||
private:
|
||||
void calc_bounding_box();
|
||||
void calc_triangles(const ExPolygon& poly);
|
||||
void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
|
||||
EType detect_type(const Pointfs& shape) const;
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void render_prusa(const std::string& key, bool bottom) const;
|
||||
void render_prusa_shader(unsigned int vertices_count, bool transparent) const;
|
||||
#else
|
||||
void render_prusa(const std::string &key, float theta, bool useVBOs) const;
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
void render_custom() const;
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void reset();
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
|
||||
#endif // slic3r_3DBed_hpp_
|
||||
|
|
@ -11,9 +11,7 @@
|
|||
#include "libslic3r/Slicing.hpp"
|
||||
#include "libslic3r/GCode/Analyzer.hpp"
|
||||
#include "slic3r/GUI/PresetBundle.hpp"
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
#include "libslic3r/Format/STL.hpp"
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -23,10 +21,8 @@
|
|||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/spin_mutex.h>
|
||||
|
|
@ -793,7 +789,7 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
|
|||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface) const
|
||||
void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface, std::function<bool(const GLVolume&)> filter_func) const
|
||||
{
|
||||
glsafe(glEnable(GL_BLEND));
|
||||
glsafe(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
|
@ -805,7 +801,7 @@ void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface)
|
|||
glsafe(glEnableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(glEnableClientState(GL_NORMAL_ARRAY));
|
||||
|
||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, std::function<bool(const GLVolume&)>());
|
||||
GLVolumesWithZList to_render = volumes_to_render(this->volumes, type, filter_func);
|
||||
for (GLVolumeWithZ& volume : to_render)
|
||||
{
|
||||
volume.first->set_render_color();
|
||||
|
|
@ -1671,27 +1667,19 @@ GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
|
|||
|
||||
GLModel::GLModel()
|
||||
: m_useVBOs(false)
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
, m_filename("")
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
{
|
||||
m_volume.shader_outside_printer_detection_enabled = false;
|
||||
}
|
||||
|
||||
GLModel::~GLModel()
|
||||
{
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
reset();
|
||||
#else
|
||||
m_volume.release_geometry();
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
}
|
||||
|
||||
void GLModel::set_color(const float* color, unsigned int size)
|
||||
{
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
::memcpy((void*)m_volume.color, (const void*)color, (size_t)(std::min((unsigned int)4, size) * sizeof(float)));
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
m_volume.set_render_color(color, size);
|
||||
}
|
||||
|
||||
|
|
@ -1725,13 +1713,11 @@ void GLModel::set_scale(const Vec3d& scale)
|
|||
m_volume.set_volume_scaling_factor(scale);
|
||||
}
|
||||
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
void GLModel::reset()
|
||||
{
|
||||
m_volume.release_geometry();
|
||||
m_filename = "";
|
||||
}
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
|
||||
void GLModel::render() const
|
||||
{
|
||||
|
|
@ -1968,7 +1954,6 @@ bool GLCurvedArrow::on_init(bool useVBOs)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
|
||||
{
|
||||
reset();
|
||||
|
|
@ -2011,7 +1996,6 @@ bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
|
|||
|
||||
return true;
|
||||
}
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
|
||||
std::string _3DScene::get_gl_info(bool format_as_html, bool extensions)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -456,7 +456,7 @@ public:
|
|||
|
||||
// Render the volumes by OpenGL.
|
||||
void render_VBOs(ERenderType type, bool disable_cullface, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
|
||||
void render_legacy(ERenderType type, bool disable_cullface) const;
|
||||
void render_legacy(ERenderType type, bool disable_cullface, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
|
||||
|
||||
// Finalize the initialization of the geometry & indices,
|
||||
// upload the geometry and indices to OpenGL VBO objects
|
||||
|
|
@ -498,20 +498,16 @@ class GLModel
|
|||
protected:
|
||||
GLVolume m_volume;
|
||||
bool m_useVBOs;
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
std::string m_filename;
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
|
||||
public:
|
||||
GLModel();
|
||||
virtual ~GLModel();
|
||||
|
||||
bool init(bool useVBOs) { return on_init(useVBOs); }
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
bool init_from_file(const std::string& filename, bool useVBOs) { return on_init_from_file(filename, useVBOs); }
|
||||
|
||||
void center_around(const Vec3d& center) { m_volume.set_volume_offset(center - m_volume.bounding_box.center()); }
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
void set_color(const float* color, unsigned int size);
|
||||
|
||||
const Vec3d& get_offset() const;
|
||||
|
|
@ -521,22 +517,16 @@ public:
|
|||
const Vec3d& get_scale() const;
|
||||
void set_scale(const Vec3d& scale);
|
||||
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
const std::string& get_filename() const { return m_filename; }
|
||||
const BoundingBoxf3& get_bounding_box() const { return m_volume.bounding_box; }
|
||||
|
||||
void reset();
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
|
||||
void render() const;
|
||||
|
||||
protected:
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
virtual bool on_init(bool useVBOs) { return false; }
|
||||
virtual bool on_init_from_file(const std::string& filename, bool useVBOs) { return false; }
|
||||
#else
|
||||
virtual bool on_init(bool useVBOs) = 0;
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
|
||||
private:
|
||||
void render_VBOs() const;
|
||||
|
|
@ -560,13 +550,11 @@ protected:
|
|||
virtual bool on_init(bool useVBOs);
|
||||
};
|
||||
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
class GLBed : public GLModel
|
||||
{
|
||||
protected:
|
||||
virtual bool on_init_from_file(const std::string& filename, bool useVBOs);
|
||||
};
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
|
||||
class _3DScene
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
#include "I18N.hpp"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
|
@ -40,17 +42,13 @@ AboutDialog::AboutDialog()
|
|||
main_sizer->Add(hsizer, 0, wxEXPAND | wxALL, 20);
|
||||
|
||||
// logo
|
||||
wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG);
|
||||
auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(logo_bmp));
|
||||
hsizer->Add(logo, 1, wxEXPAND | wxTOP | wxBOTTOM, 35);
|
||||
// wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG);
|
||||
// auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(logo_bmp));
|
||||
auto *logo = new wxStaticBitmap(this, wxID_ANY, create_scaled_bitmap("Slic3r_192px.png"));
|
||||
hsizer->Add(logo, 1, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
#ifdef __WXMSW__
|
||||
int proportion = 2;
|
||||
#else
|
||||
int proportion = 3;
|
||||
#endif
|
||||
hsizer->Add(vsizer, proportion, wxEXPAND|wxLEFT, 20);
|
||||
hsizer->Add(vsizer, 2, wxEXPAND|wxLEFT, 20);
|
||||
|
||||
// title
|
||||
{
|
||||
|
|
@ -80,6 +78,7 @@ AboutDialog::AboutDialog()
|
|||
// text
|
||||
wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO/*NEVER*/);
|
||||
{
|
||||
html->SetMinSize(wxSize(-1, 16 * wxGetApp().em_unit()));
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@ void BackgroundSlicingProcess::thread_proc()
|
|||
} catch (...) {
|
||||
error = "Unknown C++ exception.";
|
||||
}
|
||||
m_print->finalize();
|
||||
lck.lock();
|
||||
m_state = m_print->canceled() ? STATE_CANCELED : STATE_FINISHED;
|
||||
if (m_print->cancel_status() != Print::CANCELED_INTERNAL) {
|
||||
|
|
@ -362,6 +363,12 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn
|
|||
return invalidated;
|
||||
}
|
||||
|
||||
void BackgroundSlicingProcess::set_task(const PrintBase::TaskParams ¶ms)
|
||||
{
|
||||
assert(m_print != nullptr);
|
||||
m_print->set_task(params);
|
||||
}
|
||||
|
||||
// Set the output path of the G-code.
|
||||
void BackgroundSlicingProcess::schedule_export(const std::string &path)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -78,6 +78,9 @@ public:
|
|||
// Apply config over the print. Returns false, if the new config values caused any of the already
|
||||
// processed steps to be invalidated, therefore the task will need to be restarted.
|
||||
Print::ApplyStatus apply(const Model &model, const DynamicPrintConfig &config);
|
||||
// After calling the apply() function, set_task() may be called to limit the task to be processed by process().
|
||||
// This is useful for calculating SLA supports for a single object only.
|
||||
void set_task(const PrintBase::TaskParams ¶ms);
|
||||
// After calling apply, the empty() call will report whether there is anything to slice.
|
||||
bool empty() const;
|
||||
// Validate the print. Returns an empty string if valid, returns an error message if invalid.
|
||||
|
|
@ -94,6 +97,7 @@ public:
|
|||
void reset_export();
|
||||
// Once the G-code export is scheduled, the apply() methods will do nothing.
|
||||
bool is_export_scheduled() const { return ! m_export_path.empty(); }
|
||||
bool is_upload_scheduled() const { return ! m_upload_job.empty(); }
|
||||
|
||||
enum State {
|
||||
// m_thread is not running yet, or it did not reach the STATE_IDLE yet (it does not wait on the condition yet).
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
|
|||
auto sbsizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
||||
|
||||
// shape options
|
||||
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(300, -1), wxCHB_TOP);
|
||||
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition,
|
||||
wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
|
||||
sbsizer->Add(m_shape_options_book);
|
||||
|
||||
auto optgroup = init_shape_options_page(_(L("Rectangular")));
|
||||
|
|
@ -124,7 +125,7 @@ ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title)
|
|||
ConfigOptionsGroupShp optgroup;
|
||||
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings")));
|
||||
|
||||
optgroup->label_width = 100;
|
||||
optgroup->label_width = 10*wxGetApp().em_unit();//100;
|
||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
|
||||
update_shape();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class BedShapeDialog : public wxDialog
|
|||
BedShapePanel* m_panel;
|
||||
public:
|
||||
BedShapeDialog(wxWindow* parent) : wxDialog(parent, wxID_ANY, _(L("Bed Shape")),
|
||||
wxDefaultPosition, wxSize(350, 700), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {}
|
||||
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {}
|
||||
~BedShapeDialog() {}
|
||||
|
||||
void build_dialog(ConfigOptionPoints* default_pt);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "../Utils/Time.hpp"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
|
@ -94,7 +95,9 @@ static wxString generate_html_page(const Config::SnapshotDB &snapshot_db, const
|
|||
}
|
||||
|
||||
ConfigSnapshotDialog::ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db, const wxString &on_snapshot)
|
||||
: wxDialog(NULL, wxID_ANY, _(L("Configuration Snapshots")), wxDefaultPosition, wxSize(600, 500), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
|
||||
: wxDialog(NULL, wxID_ANY, _(L("Configuration Snapshots")), wxDefaultPosition,
|
||||
wxSize(45 * wxGetApp().em_unit(), 40 * wxGetApp().em_unit()),
|
||||
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX)
|
||||
{
|
||||
this->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
|
|
|
|||
|
|
@ -65,9 +65,9 @@ PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, wxSt
|
|||
|
||||
auto *sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
const auto font_title = GetFont().MakeBold().Scaled(1.3);
|
||||
const auto font_title = GetFont().MakeBold().Scaled(1.3f);
|
||||
const auto font_name = GetFont().MakeBold();
|
||||
const auto font_alt_nozzle = GetFont().Scaled(0.9);
|
||||
const auto font_alt_nozzle = GetFont().Scaled(0.9f);
|
||||
|
||||
// wxGrid appends widgets by rows, but we need to construct them in columns.
|
||||
// These vectors are used to hold the elements so that they can be appended in the right order.
|
||||
|
|
@ -789,7 +789,7 @@ void ConfigWizardIndex::on_mouse_move(wxMouseEvent &evt)
|
|||
|
||||
const ssize_t item_hover_new = pos.y / item_height();
|
||||
|
||||
if (item_hover_new < items.size() && item_hover_new != item_hover) {
|
||||
if (item_hover_new < ssize_t(items.size()) && item_hover_new != item_hover) {
|
||||
item_hover = item_hover_new;
|
||||
Refresh();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -631,14 +631,19 @@ void Choice::set_value(const boost::any& value, bool change_event)
|
|||
break;
|
||||
++idx;
|
||||
}
|
||||
idx == m_opt.enum_values.size() ?
|
||||
dynamic_cast<wxComboBox*>(window)->SetValue(text_value) :
|
||||
if (idx == m_opt.enum_values.size()) {
|
||||
// For editable Combobox under OSX is needed to set selection to -1 explicitly,
|
||||
// otherwise selection doesn't be changed
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(-1);
|
||||
dynamic_cast<wxComboBox*>(window)->SetValue(text_value);
|
||||
}
|
||||
else
|
||||
dynamic_cast<wxComboBox*>(window)->SetSelection(idx);
|
||||
break;
|
||||
}
|
||||
case coEnum: {
|
||||
int val = boost::any_cast<int>(value);
|
||||
if (m_opt_id.compare("external_fill_pattern") == 0)
|
||||
if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern")
|
||||
{
|
||||
if (!m_opt.enum_values.empty()) {
|
||||
std::string key;
|
||||
|
|
@ -707,7 +712,7 @@ boost::any& Choice::get_value()
|
|||
if (m_opt.type == coEnum)
|
||||
{
|
||||
int ret_enum = static_cast<wxComboBox*>(window)->GetSelection();
|
||||
if (m_opt_id.compare("external_fill_pattern") == 0)
|
||||
if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern")
|
||||
{
|
||||
if (!m_opt.enum_values.empty()) {
|
||||
std::string key = m_opt.enum_values[ret_enum];
|
||||
|
|
@ -785,14 +790,9 @@ boost::any& ColourPicker::get_value()
|
|||
|
||||
void PointCtrl::BUILD()
|
||||
{
|
||||
auto size = wxSize(wxDefaultSize);
|
||||
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
|
||||
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
|
||||
|
||||
auto temp = new wxBoxSizer(wxHORIZONTAL);
|
||||
// $self->wxSizer($sizer);
|
||||
//
|
||||
wxSize field_size(40, -1);
|
||||
|
||||
const wxSize field_size(4 * wxGetApp().em_unit(), -1);
|
||||
|
||||
auto default_pt = static_cast<const ConfigOptionPoints*>(m_opt.default_value)->values.at(0);
|
||||
double val = default_pt(0);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "libslic3r/Utils.hpp"
|
||||
#include "avrdude/avrdude-slic3r.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "../Utils/HexFile.hpp"
|
||||
|
|
@ -36,7 +37,6 @@
|
|||
#include <wx/collpane.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/filefn.h>
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
|
@ -446,7 +446,7 @@ void FirmwareDialog::priv::prepare_common()
|
|||
"-U", (boost::format("flash:w:0:%1%:i") % hex_file.path.string()).str(),
|
||||
}};
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
|
||||
BOOST_LOG_TRIVIAL(info) << "Preparing arguments avrdude: "
|
||||
<< std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) {
|
||||
return a + ' ' + b;
|
||||
});
|
||||
|
|
@ -492,7 +492,7 @@ void FirmwareDialog::priv::prepare_mk3()
|
|||
"-U", (boost::format("flash:w:1:%1%:i") % hex_file.path.string()).str(),
|
||||
}};
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: "
|
||||
BOOST_LOG_TRIVIAL(info) << "Preparing avrdude arguments for external flash flashing: "
|
||||
<< std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) {
|
||||
return a + ' ' + b;
|
||||
});
|
||||
|
|
@ -522,7 +522,7 @@ void FirmwareDialog::priv::prepare_mm_control()
|
|||
"-U", (boost::format("flash:w:0:%1%:i") % hex_file.path.string()).str(),
|
||||
}};
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: "
|
||||
BOOST_LOG_TRIVIAL(info) << "Preparing avrdude arguments: "
|
||||
<< std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) {
|
||||
return a + ' ' + b;
|
||||
});
|
||||
|
|
@ -588,6 +588,13 @@ void FirmwareDialog::priv::perform_upload()
|
|||
|
||||
auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId());
|
||||
auto wxmsg = wxString::FromUTF8(msg);
|
||||
#ifdef WIN32
|
||||
// The string might be in local encoding
|
||||
if (wxmsg.IsEmpty() && *msg != '\0') {
|
||||
wxmsg = wxString(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
evt->SetExtraLong(AE_MESSAGE);
|
||||
evt->SetString(std::move(wxmsg));
|
||||
wxQueueEvent(q, evt);
|
||||
|
|
@ -693,11 +700,16 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
|||
enum {
|
||||
DIALOG_MARGIN = 15,
|
||||
SPACING = 10,
|
||||
MIN_WIDTH = 600,
|
||||
MIN_HEIGHT = 200,
|
||||
MIN_HEIGHT_EXPANDED = 500,
|
||||
MIN_WIDTH = 50,
|
||||
MIN_HEIGHT = 18,
|
||||
MIN_HEIGHT_EXPANDED = 40,
|
||||
};
|
||||
|
||||
const int em = GUI::wxGetApp().em_unit();
|
||||
int min_width = MIN_WIDTH * em;
|
||||
int min_height = MIN_HEIGHT * em;
|
||||
int min_height_expanded = MIN_HEIGHT_EXPANDED * em;
|
||||
|
||||
wxFont status_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
status_font.MakeBold();
|
||||
wxFont mono_font(wxFontInfo().Family(wxFONTFAMILY_TELETYPE));
|
||||
|
|
@ -769,10 +781,10 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
|||
|
||||
auto *topsizer = new wxBoxSizer(wxVERTICAL);
|
||||
topsizer->Add(panel, 1, wxEXPAND | wxALL, DIALOG_MARGIN);
|
||||
SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT));
|
||||
SetMinSize(wxSize(min_width, min_height));
|
||||
SetSizerAndFit(topsizer);
|
||||
const auto size = GetSize();
|
||||
SetSize(std::max(size.GetWidth(), static_cast<int>(MIN_WIDTH)), std::max(size.GetHeight(), static_cast<int>(MIN_HEIGHT)));
|
||||
SetSize(std::max(size.GetWidth(), static_cast<int>(min_width)), std::max(size.GetHeight(), static_cast<int>(min_height)));
|
||||
Layout();
|
||||
|
||||
SetEscapeId(wxID_CLOSE); // To close the dialog using "Esc" button
|
||||
|
|
@ -786,13 +798,13 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
|
|||
}
|
||||
});
|
||||
|
||||
p->spoiler->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, [this](wxCollapsiblePaneEvent &evt) {
|
||||
p->spoiler->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, [=](wxCollapsiblePaneEvent &evt) {
|
||||
if (evt.GetCollapsed()) {
|
||||
this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT));
|
||||
this->SetMinSize(wxSize(min_width, min_height));
|
||||
const auto new_height = this->GetSize().GetHeight() - this->p->txt_stdout->GetSize().GetHeight();
|
||||
this->SetSize(this->GetSize().GetWidth(), new_height);
|
||||
} else {
|
||||
this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT_EXPANDED));
|
||||
this->SetMinSize(wxSize(min_width, min_height_expanded));
|
||||
}
|
||||
|
||||
this->Layout();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -8,6 +8,7 @@
|
|||
#include "3DScene.hpp"
|
||||
#include "GLToolbar.hpp"
|
||||
#include "Event.hpp"
|
||||
#include "3DBed.hpp"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
|
|
@ -25,9 +26,6 @@ class wxGLCanvas;
|
|||
// Support for Retina OpenGL on Mac OS
|
||||
#define ENABLE_RETINA_GL __APPLE__
|
||||
|
||||
class GLUquadric;
|
||||
typedef class GLUquadric GLUquadricObj;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class GLShader;
|
||||
|
|
@ -45,21 +43,6 @@ class GLGizmoBase;
|
|||
class RetinaHelper;
|
||||
#endif
|
||||
|
||||
class GeometryBuffer
|
||||
{
|
||||
std::vector<float> m_vertices;
|
||||
std::vector<float> m_tex_coords;
|
||||
|
||||
public:
|
||||
bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords);
|
||||
bool set_from_lines(const Lines& lines, float z);
|
||||
|
||||
const float* get_vertices() const;
|
||||
const float* get_tex_coords() const;
|
||||
|
||||
unsigned int get_vertices_count() const;
|
||||
};
|
||||
|
||||
class Size
|
||||
{
|
||||
int m_width;
|
||||
|
|
@ -131,6 +114,24 @@ wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent);
|
|||
wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event<bool>);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
|
||||
wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
|
||||
|
||||
// this describes events being passed from GLCanvas3D to SlaSupport gizmo
|
||||
enum class SLAGizmoEventType {
|
||||
LeftDown = 1,
|
||||
LeftUp,
|
||||
RightDown,
|
||||
Dragging,
|
||||
Delete,
|
||||
SelectAll,
|
||||
ShiftUp,
|
||||
ApplyChanges,
|
||||
DiscardChanges,
|
||||
AutomaticGeneration,
|
||||
ManualEditing
|
||||
};
|
||||
|
||||
|
||||
class GLCanvas3D
|
||||
{
|
||||
|
|
@ -196,95 +197,7 @@ class GLCanvas3D
|
|||
void set_scene_box(const BoundingBoxf3& box, GLCanvas3D& canvas);
|
||||
};
|
||||
|
||||
class Bed
|
||||
{
|
||||
public:
|
||||
enum EType : unsigned char
|
||||
{
|
||||
MK2,
|
||||
MK3,
|
||||
SL1,
|
||||
Custom,
|
||||
Num_Types
|
||||
};
|
||||
|
||||
private:
|
||||
EType m_type;
|
||||
Pointfs m_shape;
|
||||
BoundingBoxf3 m_bounding_box;
|
||||
Polygon m_polygon;
|
||||
GeometryBuffer m_triangles;
|
||||
GeometryBuffer m_gridlines;
|
||||
mutable GLTexture m_top_texture;
|
||||
mutable GLTexture m_bottom_texture;
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
mutable GLBed m_model;
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
|
||||
mutable float m_scale_factor;
|
||||
|
||||
public:
|
||||
Bed();
|
||||
|
||||
#if ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
EType get_type() const { return m_type; }
|
||||
#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
|
||||
bool is_prusa() const;
|
||||
bool is_custom() const;
|
||||
|
||||
const Pointfs& get_shape() const;
|
||||
// Return true if the bed shape changed, so the calee will update the UI.
|
||||
bool set_shape(const Pointfs& shape);
|
||||
|
||||
const BoundingBoxf3& get_bounding_box() const;
|
||||
bool contains(const Point& point) const;
|
||||
Point point_projection(const Point& point) const;
|
||||
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
void render(float theta, bool useVBOs, float scale_factor) const;
|
||||
#else
|
||||
void render(float theta, float scale_factor) const;
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
|
||||
private:
|
||||
void _calc_bounding_box();
|
||||
void _calc_triangles(const ExPolygon& poly);
|
||||
void _calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
|
||||
#if ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
EType _detect_type(const Pointfs& shape) const;
|
||||
#else
|
||||
EType _detect_type() const;
|
||||
#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
#if ENABLE_PRINT_BED_MODELS
|
||||
void _render_prusa(const std::string &key, float theta, bool useVBOs) const;
|
||||
#else
|
||||
void _render_prusa(const std::string &key, float theta) const;
|
||||
#endif // ENABLE_PRINT_BED_MODELS
|
||||
void _render_custom() const;
|
||||
#if !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
static bool _are_equal(const Pointfs& bed_1, const Pointfs& bed_2);
|
||||
#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
};
|
||||
|
||||
struct Axes
|
||||
{
|
||||
static const double Radius;
|
||||
static const double ArrowBaseRadius;
|
||||
static const double ArrowLength;
|
||||
Vec3d origin;
|
||||
Vec3d length;
|
||||
GLUquadricObj* m_quadric;
|
||||
|
||||
Axes();
|
||||
~Axes();
|
||||
|
||||
void render() const;
|
||||
|
||||
private:
|
||||
void render_axis(double length) const;
|
||||
};
|
||||
|
||||
#if !ENABLE_TEXTURES_FROM_SVG
|
||||
class Shader
|
||||
{
|
||||
GLShader* m_shader;
|
||||
|
|
@ -308,6 +221,7 @@ class GLCanvas3D
|
|||
private:
|
||||
void _reset();
|
||||
};
|
||||
#endif // !ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
class LayersEditing
|
||||
{
|
||||
|
|
@ -788,12 +702,8 @@ private:
|
|||
|
||||
void set_flattening_data(const ModelObject* model_object);
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection);
|
||||
#else
|
||||
void set_model_object_ptr(ModelObject* model_object);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void clicked_on_object(const Vec2d& mouse_position);
|
||||
bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false);
|
||||
void delete_current_grabber(bool delete_all = false);
|
||||
|
||||
void render_current_gizmo(const Selection& selection) const;
|
||||
|
|
@ -835,18 +745,32 @@ private:
|
|||
|
||||
class WarningTexture : public GUI::GLTexture
|
||||
{
|
||||
public:
|
||||
WarningTexture();
|
||||
|
||||
enum Warning {
|
||||
ObjectOutside,
|
||||
ToolpathOutside,
|
||||
SomethingNotShown
|
||||
};
|
||||
|
||||
// Sets a warning of the given type to be active/inactive. If several warnings are active simultaneously,
|
||||
// only the last one is shown (decided by the order in the enum above).
|
||||
void activate(WarningTexture::Warning warning, bool state, const GLCanvas3D& canvas);
|
||||
void render(const GLCanvas3D& canvas) const;
|
||||
|
||||
private:
|
||||
static const unsigned char Background_Color[3];
|
||||
static const unsigned char Opacity;
|
||||
|
||||
int m_original_width;
|
||||
int m_original_height;
|
||||
|
||||
public:
|
||||
WarningTexture();
|
||||
// Information about which warnings are currently active.
|
||||
std::vector<Warning> m_warnings;
|
||||
|
||||
bool generate(const std::string& msg, const GLCanvas3D& canvas);
|
||||
|
||||
void render(const GLCanvas3D& canvas) const;
|
||||
// Generates the texture with given text.
|
||||
bool _generate(const std::string& msg, const GLCanvas3D& canvas);
|
||||
};
|
||||
|
||||
class LegendTexture : public GUI::GLTexture
|
||||
|
|
@ -884,8 +808,7 @@ private:
|
|||
WarningTexture m_warning_texture;
|
||||
wxTimer m_timer;
|
||||
Camera m_camera;
|
||||
Bed m_bed;
|
||||
Axes m_axes;
|
||||
Bed3D* m_bed;
|
||||
LayersEditing m_layers_editing;
|
||||
Shader m_shader;
|
||||
Mouse m_mouse;
|
||||
|
|
@ -907,11 +830,7 @@ private:
|
|||
bool m_dirty;
|
||||
bool m_initialized;
|
||||
bool m_use_VBOs;
|
||||
#if ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
bool m_requires_zoom_to_bed;
|
||||
#else
|
||||
bool m_force_zoom_to_bed_enabled;
|
||||
#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
bool m_apply_zoom_to_volumes_filter;
|
||||
mutable int m_hover_volume_id;
|
||||
bool m_toolbar_action_running;
|
||||
|
|
@ -923,6 +842,8 @@ private:
|
|||
bool m_multisample_allowed;
|
||||
bool m_regenerate_volumes;
|
||||
bool m_moving;
|
||||
bool m_tab_down;
|
||||
bool m_render_sla_auxiliaries;
|
||||
|
||||
std::string m_color_by;
|
||||
|
||||
|
|
@ -943,6 +864,8 @@ public:
|
|||
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
|
||||
const wxGLCanvas* get_wxglcanvas() const { return m_canvas; }
|
||||
|
||||
void set_bed(Bed3D* bed) { m_bed = bed; }
|
||||
|
||||
void set_view_toolbar(GLToolbar* toolbar) { m_view_toolbar = toolbar; }
|
||||
|
||||
bool init(bool useVBOs, bool use_legacy_opengl);
|
||||
|
|
@ -954,6 +877,9 @@ public:
|
|||
void reset_volumes();
|
||||
int check_volumes_outside_state() const;
|
||||
|
||||
void toggle_sla_auxiliaries_visibility(bool visible);
|
||||
void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
|
||||
|
||||
void set_config(const DynamicPrintConfig* config);
|
||||
void set_process(BackgroundSlicingProcess* process);
|
||||
void set_model(Model* model);
|
||||
|
|
@ -961,12 +887,7 @@ public:
|
|||
const Selection& get_selection() const { return m_selection; }
|
||||
Selection& get_selection() { return m_selection; }
|
||||
|
||||
// Set the bed shape to a single closed 2D polygon(array of two element arrays),
|
||||
// triangulate the bed and store the triangles into m_bed.m_triangles,
|
||||
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
|
||||
// Sets m_bed.m_polygon to limit the object placement.
|
||||
void set_bed_shape(const Pointfs& shape);
|
||||
void set_bed_axes_length(double length);
|
||||
void bed_shape_changed();
|
||||
|
||||
void set_clipping_plane(unsigned int id, const ClippingPlane& plane)
|
||||
{
|
||||
|
|
@ -991,15 +912,11 @@ public:
|
|||
bool is_reload_delayed() const;
|
||||
|
||||
void enable_layers_editing(bool enable);
|
||||
void enable_warning_texture(bool enable);
|
||||
void enable_legend_texture(bool enable);
|
||||
void enable_picking(bool enable);
|
||||
void enable_moving(bool enable);
|
||||
void enable_gizmos(bool enable);
|
||||
void enable_toolbar(bool enable);
|
||||
#if !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
void enable_force_zoom_to_bed(bool enable);
|
||||
#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
void enable_dynamic_background(bool enable);
|
||||
void allow_multisample(bool allow);
|
||||
|
||||
|
|
@ -1050,6 +967,7 @@ public:
|
|||
void on_size(wxSizeEvent& evt);
|
||||
void on_idle(wxIdleEvent& evt);
|
||||
void on_char(wxKeyEvent& evt);
|
||||
void on_key(wxKeyEvent& evt);
|
||||
void on_mouse_wheel(wxMouseEvent& evt);
|
||||
void on_timer(wxTimerEvent& evt);
|
||||
void on_mouse(wxMouseEvent& evt);
|
||||
|
|
@ -1084,9 +1002,6 @@ public:
|
|||
|
||||
private:
|
||||
bool _is_shown_on_screen() const;
|
||||
#if !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
void _force_zoom_to_bed();
|
||||
#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
|
||||
bool _init_toolbar();
|
||||
|
||||
|
|
@ -1176,8 +1091,7 @@ private:
|
|||
void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
|
||||
|
||||
// generates a warning texture containing the given message
|
||||
void _generate_warning_texture(const std::string& msg);
|
||||
void _reset_warning_texture();
|
||||
void _set_warning_texture(WarningTexture::Warning warning, bool state);
|
||||
|
||||
bool _is_any_volume_outside() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent)
|
|||
attribList[4] = 0;
|
||||
}
|
||||
|
||||
return new wxGLCanvas(parent, wxID_ANY, attribList);
|
||||
return new wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
|
||||
}
|
||||
|
||||
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/SLA/SLASupportTree.hpp"
|
||||
#include "libslic3r/SLA/SLACommon.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
|
@ -1445,8 +1445,8 @@ void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const
|
|||
{
|
||||
const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix();
|
||||
::glPushMatrix();
|
||||
::glMultMatrixd(m.data());
|
||||
::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z());
|
||||
::glMultMatrixd(m.data());
|
||||
if (this->is_plane_update_necessary())
|
||||
const_cast<GLGizmoFlatten*>(this)->update_planes();
|
||||
for (int i = 0; i < (int)m_planes.size(); ++i)
|
||||
|
|
@ -1479,8 +1479,8 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio
|
|||
{
|
||||
const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix();
|
||||
::glPushMatrix();
|
||||
::glMultMatrixd(m.data());
|
||||
::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z());
|
||||
::glMultMatrixd(m.data());
|
||||
if (this->is_plane_update_necessary())
|
||||
const_cast<GLGizmoFlatten*>(this)->update_planes();
|
||||
for (int i = 0; i < (int)m_planes.size(); ++i)
|
||||
|
|
@ -1514,7 +1514,7 @@ void GLGizmoFlatten::update_planes()
|
|||
TriangleMesh ch;
|
||||
for (const ModelVolume* vol : m_model_object->volumes)
|
||||
{
|
||||
if (vol->type() != ModelVolume::Type::MODEL_PART)
|
||||
if (vol->type() != ModelVolumeType::MODEL_PART)
|
||||
continue;
|
||||
TriangleMesh vol_ch = vol->get_convex_hull();
|
||||
vol_ch.transform(vol->get_matrix());
|
||||
|
|
@ -1741,28 +1741,20 @@ Vec3d GLGizmoFlatten::get_flattening_normal() const
|
|||
}
|
||||
|
||||
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent)
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
: GLGizmoBase(parent), m_starting_center(Vec3d::Zero()), m_quadric(nullptr)
|
||||
#else
|
||||
: GLGizmoBase(parent), m_starting_center(Vec3d::Zero())
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
{
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
m_quadric = ::gluNewQuadric();
|
||||
if (m_quadric != nullptr)
|
||||
// using GLU_FILL does not work when the instance's transformation
|
||||
// contains mirroring (normals are reverted)
|
||||
::gluQuadricDrawStyle(m_quadric, GLU_FILL);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
}
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
GLGizmoSlaSupports::~GLGizmoSlaSupports()
|
||||
{
|
||||
if (m_quadric != nullptr)
|
||||
::gluDeleteQuadric(m_quadric);
|
||||
}
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
bool GLGizmoSlaSupports::on_init()
|
||||
{
|
||||
|
|
@ -1782,7 +1774,6 @@ bool GLGizmoSlaSupports::on_init()
|
|||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection)
|
||||
{
|
||||
m_starting_center = Vec3d::Zero();
|
||||
|
|
@ -1791,217 +1782,165 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G
|
|||
if (selection.is_empty())
|
||||
m_old_instance_id = -1;
|
||||
|
||||
if ((model_object != nullptr) && selection.is_from_single_instance())
|
||||
m_active_instance = selection.get_instance_idx();
|
||||
|
||||
if (model_object && selection.is_from_single_instance())
|
||||
{
|
||||
if (is_mesh_update_necessary())
|
||||
update_mesh();
|
||||
|
||||
// If there are no points, let's ask the backend if it calculated some.
|
||||
if (model_object->sla_support_points.empty() && m_parent.sla_print()->is_step_done(slaposSupportPoints)) {
|
||||
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
|
||||
if (po->model_object()->id() == model_object->id()) {
|
||||
const Eigen::MatrixXd& points = po->get_support_points();
|
||||
for (unsigned int i=0; i<points.rows();++i)
|
||||
model_object->sla_support_points.push_back(Vec3f(po->trafo().inverse().cast<float>() * Vec3f(points(i,0), points(i,1), points(i,2))));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_editing_mode_cache.empty())
|
||||
get_data_from_backend();
|
||||
|
||||
if (m_model_object != m_old_model_object)
|
||||
m_editing_mode = false;
|
||||
if (m_state == On) {
|
||||
m_parent.toggle_model_objects_visibility(false);
|
||||
m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object)
|
||||
{
|
||||
if (model_object != nullptr) {
|
||||
m_starting_center = Vec3d::Zero();
|
||||
m_model_object = model_object;
|
||||
|
||||
int selected_instance = m_parent.get_selection().get_instance_idx();
|
||||
assert(selected_instance < (int)model_object->instances.size());
|
||||
|
||||
m_instance_matrix = model_object->instances[selected_instance]->get_matrix();
|
||||
if (is_mesh_update_necessary())
|
||||
update_mesh();
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const
|
||||
{
|
||||
::glEnable(GL_BLEND);
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
|
||||
#if !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
// the dragged_offset is a vector measuring where was the object moved
|
||||
// with the gizmo being on. This is reset in set_model_object_ptr and
|
||||
// does not work correctly when there are multiple copies.
|
||||
|
||||
if (m_starting_center == Vec3d::Zero())
|
||||
m_starting_center = selection.get_bounding_box().center();
|
||||
Vec3d dragged_offset = selection.get_bounding_box().center() - m_starting_center;
|
||||
#endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
|
||||
for (auto& g : m_grabbers) {
|
||||
g.color[0] = 1.f;
|
||||
g.color[1] = 0.f;
|
||||
g.color[2] = 0.f;
|
||||
}
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
render_grabbers(selection, false);
|
||||
#else
|
||||
//::glTranslatef((GLfloat)dragged_offset(0), (GLfloat)dragged_offset(1), (GLfloat)dragged_offset(2));
|
||||
render_grabbers(false);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
render_points(selection, false);
|
||||
render_selection_rectangle();
|
||||
|
||||
#if !ENABLE_IMGUI
|
||||
render_tooltip_texture();
|
||||
#endif // not ENABLE_IMGUI
|
||||
|
||||
::glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::render_selection_rectangle() const
|
||||
{
|
||||
if (!m_selection_rectangle_active)
|
||||
return;
|
||||
|
||||
::glLineWidth(1.5f);
|
||||
float render_color[3] = {1.f, 0.f, 0.f};
|
||||
::glColor3fv(render_color);
|
||||
|
||||
::glPushAttrib(GL_TRANSFORM_BIT); // remember current MatrixMode
|
||||
|
||||
::glMatrixMode(GL_MODELVIEW); // cache modelview matrix and set to identity
|
||||
::glPushMatrix();
|
||||
::glLoadIdentity();
|
||||
|
||||
::glMatrixMode(GL_PROJECTION); // cache projection matrix and set to identity
|
||||
::glPushMatrix();
|
||||
::glLoadIdentity();
|
||||
|
||||
::glOrtho(0.f, m_canvas_width, m_canvas_height, 0.f, -1.f, 1.f); // set projection matrix so that world coords = window coords
|
||||
|
||||
// render the selection rectangle (window coordinates):
|
||||
::glPushAttrib(GL_ENABLE_BIT);
|
||||
::glLineStipple(4, 0xAAAA);
|
||||
::glEnable(GL_LINE_STIPPLE);
|
||||
|
||||
::glBegin(GL_LINE_LOOP);
|
||||
::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f);
|
||||
::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f);
|
||||
::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f);
|
||||
::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f);
|
||||
::glEnd();
|
||||
::glPopAttrib();
|
||||
|
||||
::glPopMatrix(); // restore former projection matrix
|
||||
::glMatrixMode(GL_MODELVIEW);
|
||||
::glPopMatrix(); // restore former modelview matrix
|
||||
::glPopAttrib(); // restore former MatrixMode
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::on_render_for_picking(const GLCanvas3D::Selection& selection) const
|
||||
{
|
||||
::glEnable(GL_DEPTH_TEST);
|
||||
for (unsigned int i=0; i<m_grabbers.size(); ++i) {
|
||||
m_grabbers[i].color[0] = 1.0f;
|
||||
m_grabbers[i].color[1] = 1.0f;
|
||||
m_grabbers[i].color[2] = picking_color_component(i);
|
||||
}
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
render_grabbers(selection, true);
|
||||
#else
|
||||
render_grabbers(true);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
render_points(selection, true);
|
||||
}
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void GLGizmoSlaSupports::render_grabbers(const GLCanvas3D::Selection& selection, bool picking) const
|
||||
void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, bool picking) const
|
||||
{
|
||||
if (m_quadric == nullptr)
|
||||
if (m_quadric == nullptr || !selection.is_from_single_instance())
|
||||
return;
|
||||
|
||||
if (!selection.is_from_single_instance())
|
||||
return;
|
||||
if (!picking)
|
||||
::glEnable(GL_LIGHTING);
|
||||
|
||||
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
double z_shift = v->get_sla_shift_z();
|
||||
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
double z_shift = vol->get_sla_shift_z();
|
||||
const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
|
||||
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix();
|
||||
|
||||
::glPushMatrix();
|
||||
::glTranslated(0.0, 0.0, z_shift);
|
||||
|
||||
const Transform3d& m = selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_transformation().get_matrix();
|
||||
::glMultMatrixd(m.data());
|
||||
|
||||
if (!picking)
|
||||
::glEnable(GL_LIGHTING);
|
||||
::glMultMatrixd(instance_matrix.data());
|
||||
|
||||
float render_color[3];
|
||||
for (int i = 0; i < (int)m_grabbers.size(); ++i)
|
||||
for (int i = 0; i < (int)m_editing_mode_cache.size(); ++i)
|
||||
{
|
||||
// first precalculate the grabber position in world coordinates, so that the grabber
|
||||
// is not scaled with the object (as it would be if rendered with current gl matrix).
|
||||
Eigen::Matrix<GLfloat, 4, 4> glmatrix;
|
||||
glGetFloatv (GL_MODELVIEW_MATRIX, glmatrix.data());
|
||||
Eigen::Matrix<float, 4, 1> grabber_pos;
|
||||
for (int j=0; j<3; ++j)
|
||||
grabber_pos(j) = m_grabbers[i].center(j);
|
||||
grabber_pos[3] = 1.f;
|
||||
Eigen::Matrix<float, 4, 1> grabber_world_position = glmatrix * grabber_pos;
|
||||
const sla::SupportPoint& support_point = m_editing_mode_cache[i].first;
|
||||
const bool& point_selected = m_editing_mode_cache[i].second;
|
||||
|
||||
if (!picking && (m_hover_id == i))
|
||||
{
|
||||
render_color[0] = 1.0f - m_grabbers[i].color[0];
|
||||
render_color[1] = 1.0f - m_grabbers[i].color[1];
|
||||
render_color[2] = 1.0f - m_grabbers[i].color[2];
|
||||
// First decide about the color of the point.
|
||||
if (picking) {
|
||||
render_color[0] = 1.0f;
|
||||
render_color[1] = 1.0f;
|
||||
render_color[2] = picking_color_component(i);
|
||||
}
|
||||
else {
|
||||
if ((m_hover_id == i && m_editing_mode)) { // ignore hover state unless editing mode is active
|
||||
render_color[0] = 0.f;
|
||||
render_color[1] = 1.0f;
|
||||
render_color[2] = 1.0f;
|
||||
}
|
||||
else { // neigher hover nor picking
|
||||
bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].first.is_new_island;
|
||||
if (m_editing_mode) {
|
||||
render_color[0] = point_selected ? 1.0f : (supports_new_island ? 0.3f : 0.7f);
|
||||
render_color[1] = point_selected ? 0.3f : (supports_new_island ? 0.3f : 0.7f);
|
||||
render_color[2] = point_selected ? 0.3f : (supports_new_island ? 1.0f : 0.7f);
|
||||
}
|
||||
else
|
||||
::memcpy((void*)render_color, (const void*)m_grabbers[i].color, 3 * sizeof(float));
|
||||
|
||||
for (unsigned char i=0; i<3; ++i) render_color[i] = 0.5f;
|
||||
}
|
||||
}
|
||||
::glColor3fv(render_color);
|
||||
float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f};
|
||||
::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive);
|
||||
|
||||
// Now render the sphere. Inverse matrix of the instance scaling is applied so that the
|
||||
// sphere does not scale with the object.
|
||||
::glPushMatrix();
|
||||
::glLoadIdentity();
|
||||
::glTranslated(grabber_world_position(0), grabber_world_position(1), grabber_world_position(2) + z_shift);
|
||||
const float diameter = 0.8f;
|
||||
::gluSphere(m_quadric, diameter/2.f, 64, 36);
|
||||
::glTranslated(support_point.pos(0), support_point.pos(1), support_point.pos(2));
|
||||
::glMultMatrixd(instance_scaling_matrix_inverse.data());
|
||||
::gluSphere(m_quadric, m_editing_mode_cache[i].first.head_front_radius * RenderPointScale, 64, 36);
|
||||
::glPopMatrix();
|
||||
}
|
||||
|
||||
if (!picking)
|
||||
::glDisable(GL_LIGHTING);
|
||||
|
||||
::glPopMatrix();
|
||||
}
|
||||
#else
|
||||
void GLGizmoSlaSupports::render_grabbers(bool picking) const
|
||||
{
|
||||
if (m_parent.get_selection().is_empty())
|
||||
return;
|
||||
|
||||
float z_shift = m_parent.get_selection().get_volume(0)->get_sla_shift_z();
|
||||
::glTranslatef((GLfloat)0, (GLfloat)0, (GLfloat)z_shift);
|
||||
|
||||
int selected_instance = m_parent.get_selection().get_instance_idx();
|
||||
assert(selected_instance < (int)m_model_object->instances.size());
|
||||
|
||||
float render_color_inactive[3] = { 0.5f, 0.5f, 0.5f };
|
||||
|
||||
for (const ModelInstance* inst : m_model_object->instances) {
|
||||
bool active = inst == m_model_object->instances[selected_instance];
|
||||
if (picking && ! active)
|
||||
continue;
|
||||
for (int i = 0; i < (int)m_grabbers.size(); ++i)
|
||||
{
|
||||
if (!m_grabbers[i].enabled)
|
||||
continue;
|
||||
|
||||
float render_color[3];
|
||||
if (! picking && active && m_hover_id == i) {
|
||||
render_color[0] = 1.0f - m_grabbers[i].color[0];
|
||||
render_color[1] = 1.0f - m_grabbers[i].color[1];
|
||||
render_color[2] = 1.0f - m_grabbers[i].color[2];
|
||||
// Reset emissive component to zero (the default value)
|
||||
float render_color_emissive[4] = { 0.f, 0.f, 0.f, 1.f };
|
||||
::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive);
|
||||
}
|
||||
else
|
||||
::memcpy((void*)render_color, active ? (const void*)m_grabbers[i].color : (const void*)render_color_inactive, 3 * sizeof(float));
|
||||
if (!picking)
|
||||
::glEnable(GL_LIGHTING);
|
||||
::glColor3f((GLfloat)render_color[0], (GLfloat)render_color[1], (GLfloat)render_color[2]);
|
||||
::glPushMatrix();
|
||||
Vec3d center = inst->get_matrix() * m_grabbers[i].center;
|
||||
::glTranslatef((GLfloat)center(0), (GLfloat)center(1), (GLfloat)center(2));
|
||||
GLUquadricObj *quadric;
|
||||
quadric = ::gluNewQuadric();
|
||||
::gluQuadricDrawStyle(quadric, GLU_FILL );
|
||||
::gluSphere( quadric , 0.4, 64 , 32 );
|
||||
::gluDeleteQuadric(quadric);
|
||||
::glPopMatrix();
|
||||
|
||||
if (!picking)
|
||||
::glDisable(GL_LIGHTING);
|
||||
}
|
||||
}
|
||||
|
||||
::glTranslatef((GLfloat)0, (GLfloat)0, (GLfloat)-z_shift);
|
||||
::glPopMatrix();
|
||||
}
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
|
||||
{
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
return (m_state == On) && (m_model_object != nullptr) && (m_model_object != m_old_model_object) && !m_model_object->instances.empty();
|
||||
#else
|
||||
return m_state == On && m_model_object && !m_model_object->instances.empty() && !m_instance_matrix.isApprox(m_source_data.matrix);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
//if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix))
|
||||
// return false;
|
||||
|
||||
// following should detect direct mesh changes (can be removed after the mesh is made completely immutable):
|
||||
/*const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex();
|
||||
Vec3d first_point((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]);
|
||||
if (first_point != m_source_data.mesh_first_point)
|
||||
return true;*/
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::update_mesh()
|
||||
|
|
@ -2027,27 +1966,14 @@ void GLGizmoSlaSupports::update_mesh()
|
|||
m_AABB = igl::AABB<Eigen::MatrixXf,3>();
|
||||
m_AABB.init(m_V, m_F);
|
||||
|
||||
#if !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
m_source_data.matrix = m_instance_matrix;
|
||||
#endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
// we'll now reload Grabbers (selection might have changed):
|
||||
m_grabbers.clear();
|
||||
|
||||
for (const Vec3f& point : m_model_object->sla_support_points) {
|
||||
m_grabbers.push_back(Grabber());
|
||||
m_grabbers.back().center = point.cast<double>();
|
||||
}
|
||||
// we'll now reload support points (selection might have changed):
|
||||
editing_mode_reload_cache();
|
||||
}
|
||||
|
||||
Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
||||
{
|
||||
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
if (m_V.size() == 0)
|
||||
#else
|
||||
if (m_V.size() == 0 || is_mesh_update_necessary())
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
update_mesh();
|
||||
|
||||
Eigen::Matrix<GLint, 4, 1, Eigen::DontAlign> viewport;
|
||||
|
|
@ -2064,20 +1990,15 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
|||
|
||||
igl::Hit hit;
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
const GLCanvas3D::Selection& selection = m_parent.get_selection();
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
double z_offset = volume->get_sla_shift_z();
|
||||
#else
|
||||
double z_offset = m_parent.get_selection().get_volume(0)->get_sla_shift_z();
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
point1(2) -= z_offset;
|
||||
point2(2) -= z_offset;
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
Transform3d inv = volume->get_instance_transformation().get_matrix().inverse();
|
||||
#else
|
||||
Transform3d inv = m_instance_matrix.inverse();
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
|
||||
point1 = inv * point1;
|
||||
point2 = inv * point2;
|
||||
|
||||
|
|
@ -2089,68 +2010,202 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
|||
return bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::clicked_on_object(const Vec2d& mouse_position)
|
||||
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
|
||||
// The gizmo has an opportunity to react - if it does, it should return true so that the Canvas3D is
|
||||
// aware that the event was reacted to and stops trying to make different sense of it. If the gizmo
|
||||
// concludes that the event was not intended for it, it should return false.
|
||||
bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down)
|
||||
{
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
if (m_editing_mode) {
|
||||
|
||||
// left down - show the selection rectangle:
|
||||
if (action == SLAGizmoEventType::LeftDown && shift_down) {
|
||||
if (m_hover_id == -1) {
|
||||
m_selection_rectangle_active = true;
|
||||
m_selection_rectangle_start_corner = mouse_position;
|
||||
m_selection_rectangle_end_corner = mouse_position;
|
||||
m_canvas_width = m_parent.get_canvas_size().get_width();
|
||||
m_canvas_height = m_parent.get_canvas_size().get_height();
|
||||
}
|
||||
else
|
||||
select_point(m_hover_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// dragging the selection rectangle:
|
||||
if (action == SLAGizmoEventType::Dragging && m_selection_rectangle_active) {
|
||||
m_selection_rectangle_end_corner = mouse_position;
|
||||
return true;
|
||||
}
|
||||
|
||||
// mouse up without selection rectangle - place point on the mesh:
|
||||
if (action == SLAGizmoEventType::LeftUp && !m_selection_rectangle_active && !shift_down) {
|
||||
if (m_ignore_up_event) {
|
||||
m_ignore_up_event = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
int instance_id = m_parent.get_selection().get_instance_idx();
|
||||
if (m_old_instance_id != instance_id)
|
||||
{
|
||||
bool something_selected = (m_old_instance_id != -1);
|
||||
m_old_instance_id = instance_id;
|
||||
if (something_selected)
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (instance_id == -1)
|
||||
return;
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
return false;
|
||||
|
||||
// If there is some selection, don't add new point and deselect everything instead.
|
||||
if (m_selection_empty) {
|
||||
Vec3f new_pos;
|
||||
try {
|
||||
new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new grabber in that case
|
||||
new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new point in that case
|
||||
m_editing_mode_cache.emplace_back(std::make_pair(sla::SupportPoint(new_pos, m_new_point_head_diameter/2.f, false), false));
|
||||
m_unsaved_changes = true;
|
||||
}
|
||||
catch (...) { // not clicked on object
|
||||
return true; // prevents deselection of the gizmo by GLCanvas3D
|
||||
}
|
||||
catch (...) { return; }
|
||||
|
||||
m_grabbers.push_back(Grabber());
|
||||
m_grabbers.back().center = new_pos.cast<double>();
|
||||
m_model_object->sla_support_points.push_back(new_pos);
|
||||
|
||||
// This should trigger the support generation
|
||||
// wxGetApp().plater()->reslice();
|
||||
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::delete_current_grabber(bool delete_all)
|
||||
{
|
||||
if (delete_all) {
|
||||
m_grabbers.clear();
|
||||
m_model_object->sla_support_points.clear();
|
||||
|
||||
// This should trigger the support generation
|
||||
// wxGetApp().plater()->reslice();
|
||||
}
|
||||
else
|
||||
if (m_hover_id != -1) {
|
||||
m_grabbers.erase(m_grabbers.begin() + m_hover_id);
|
||||
m_model_object->sla_support_points.erase(m_model_object->sla_support_points.begin() + m_hover_id);
|
||||
m_hover_id = -1;
|
||||
select_point(NoPoints);
|
||||
|
||||
// This should trigger the support generation
|
||||
// wxGetApp().plater()->reslice();
|
||||
return true;
|
||||
}
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
|
||||
// left up with selection rectangle - select points inside the rectangle:
|
||||
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp)
|
||||
&& m_selection_rectangle_active) {
|
||||
if (action == SLAGizmoEventType::ShiftUp)
|
||||
m_ignore_up_event = true;
|
||||
const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix();
|
||||
GLint viewport[4];
|
||||
::glGetIntegerv(GL_VIEWPORT, viewport);
|
||||
GLdouble modelview_matrix[16];
|
||||
::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix);
|
||||
GLdouble projection_matrix[16];
|
||||
::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
|
||||
|
||||
const GLCanvas3D::Selection& selection = m_parent.get_selection();
|
||||
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
|
||||
double z_offset = volume->get_sla_shift_z();
|
||||
|
||||
// bounding box created from the rectangle corners - will take care of order of the corners
|
||||
BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast<int>()), Point(m_selection_rectangle_end_corner.cast<int>())});
|
||||
|
||||
const Transform3d& instance_matrix_no_translation = volume->get_instance_transformation().get_matrix(true);
|
||||
// we'll recover current look direction from the modelview matrix (in world coords)...
|
||||
Vec3f direction_to_camera(modelview_matrix[2], modelview_matrix[6], modelview_matrix[10]);
|
||||
// ...and transform it to model coords.
|
||||
direction_to_camera = (instance_matrix_no_translation.inverse().cast<float>() * direction_to_camera).normalized().eval();
|
||||
|
||||
// Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh:
|
||||
for (unsigned int i=0; i<m_editing_mode_cache.size(); ++i) {
|
||||
const sla::SupportPoint &support_point = m_editing_mode_cache[i].first;
|
||||
Vec3f pos = instance_matrix.cast<float>() * support_point.pos;
|
||||
pos(2) += z_offset;
|
||||
GLdouble out_x, out_y, out_z;
|
||||
::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z);
|
||||
out_y = m_canvas_height - out_y;
|
||||
|
||||
if (rectangle.contains(Point(out_x, out_y))) {
|
||||
bool is_obscured = false;
|
||||
// Cast a ray in the direction of the camera and look for intersection with the mesh:
|
||||
std::vector<igl::Hit> hits;
|
||||
// Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
|
||||
if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera * (support_point.head_front_radius + EPSILON), direction_to_camera, hits))
|
||||
// FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
|
||||
// Also, the threshold is in mesh coordinates, not in actual dimensions.
|
||||
if (hits.size() > 1 || hits.front().t > 0.001f)
|
||||
is_obscured = true;
|
||||
|
||||
if (!is_obscured)
|
||||
select_point(i);
|
||||
}
|
||||
}
|
||||
m_selection_rectangle_active = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::Delete) {
|
||||
// delete key pressed
|
||||
delete_selected_points();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::ApplyChanges) {
|
||||
editing_mode_apply_changes();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::DiscardChanges) {
|
||||
editing_mode_discard_changes();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::RightDown) {
|
||||
if (m_hover_id != -1) {
|
||||
select_point(NoPoints);
|
||||
select_point(m_hover_id);
|
||||
delete_selected_points();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::SelectAll) {
|
||||
select_point(AllPoints);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_editing_mode) {
|
||||
if (action == SLAGizmoEventType::AutomaticGeneration) {
|
||||
auto_generate();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::ManualEditing) {
|
||||
switch_to_editing_mode();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::delete_selected_points()
|
||||
{
|
||||
if (!m_editing_mode)
|
||||
return;
|
||||
|
||||
for (unsigned int idx=0; idx<m_editing_mode_cache.size(); ++idx) {
|
||||
if (m_editing_mode_cache[idx].second && (!m_editing_mode_cache[idx].first.is_new_island || !m_lock_unique_islands)) {
|
||||
m_editing_mode_cache.erase(m_editing_mode_cache.begin() + (idx--));
|
||||
m_unsaved_changes = true;
|
||||
}
|
||||
// This should trigger the support generation
|
||||
// wxGetApp().plater()->reslice_SLA_supports(*m_model_object);
|
||||
}
|
||||
|
||||
select_point(NoPoints);
|
||||
|
||||
//m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection)
|
||||
{
|
||||
if (m_hover_id != -1 && data.mouse_pos) {
|
||||
if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].first.is_new_island || !m_lock_unique_islands)) {
|
||||
Vec3f new_pos;
|
||||
try {
|
||||
new_pos = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1)));
|
||||
}
|
||||
catch (...) { return; }
|
||||
m_grabbers[m_hover_id].center = new_pos.cast<double>();
|
||||
m_model_object->sla_support_points[m_hover_id] = new_pos;
|
||||
m_editing_mode_cache[m_hover_id].first.pos = new_pos;
|
||||
m_editing_mode_cache[m_hover_id].first.is_new_island = false;
|
||||
m_unsaved_changes = true;
|
||||
// Do not update immediately, wait until the mouse is released.
|
||||
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
|
|
@ -2196,37 +2251,119 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, const GLCanvas
|
|||
RENDER_AGAIN:
|
||||
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
||||
m_imgui->set_next_window_bg_alpha(0.5f);
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove |/* ImGuiWindowFlags_NoResize | */ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
ImGui::PushItemWidth(100.0f);
|
||||
|
||||
bool force_refresh = false;
|
||||
bool remove_selected = false;
|
||||
|
||||
if (m_editing_mode) {
|
||||
m_imgui->text(_(L("Left mouse click - add point")));
|
||||
m_imgui->text(_(L("Right mouse click - remove point")));
|
||||
m_imgui->text(" ");
|
||||
m_imgui->text(_(L("Shift + Left (+ drag) - select point(s)")));
|
||||
m_imgui->text(" "); // vertical gap
|
||||
|
||||
bool generate = m_imgui->button(_(L("Generate points automatically")));
|
||||
bool remove_all_clicked = m_imgui->button(_(L("Remove all points")) + (m_model_object == nullptr ? "" : " (" + std::to_string(m_model_object->sla_support_points.size())+")"));
|
||||
std::vector<wxString> options = {"0.2", "0.4", "0.6", "0.8", "1.0"};
|
||||
std::stringstream ss;
|
||||
ss << std::setprecision(1) << m_new_point_head_diameter;
|
||||
wxString str = ss.str();
|
||||
|
||||
bool old_combo_state = m_combo_box_open;
|
||||
m_combo_box_open = m_imgui->combo(_(L("Head diameter")), options, str);
|
||||
force_refresh |= (old_combo_state != m_combo_box_open);
|
||||
|
||||
float current_number = atof(str);
|
||||
if (old_combo_state && !m_combo_box_open) // closing the combo must always change the sizes (even if the selection did not change)
|
||||
for (auto& point_and_selection : m_editing_mode_cache)
|
||||
if (point_and_selection.second) {
|
||||
point_and_selection.first.head_front_radius = current_number / 2.f;
|
||||
m_unsaved_changes = true;
|
||||
}
|
||||
|
||||
if (std::abs(current_number - m_new_point_head_diameter) > 0.001) {
|
||||
force_refresh = true;
|
||||
m_new_point_head_diameter = current_number;
|
||||
}
|
||||
|
||||
bool changed = m_lock_unique_islands;
|
||||
m_imgui->checkbox(_(L("Lock supports under new islands")), m_lock_unique_islands);
|
||||
force_refresh |= changed != m_lock_unique_islands;
|
||||
|
||||
m_imgui->disabled_begin(m_selection_empty);
|
||||
remove_selected = m_imgui->button(_(L("Remove selected points")));
|
||||
m_imgui->disabled_end();
|
||||
|
||||
m_imgui->text(" "); // vertical gap
|
||||
|
||||
bool apply_changes = m_imgui->button(_(L("Apply changes")));
|
||||
if (apply_changes) {
|
||||
editing_mode_apply_changes();
|
||||
force_refresh = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
bool discard_changes = m_imgui->button(_(L("Discard changes")));
|
||||
if (discard_changes) {
|
||||
editing_mode_discard_changes();
|
||||
force_refresh = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* ImGui::PushItemWidth(50.0f);
|
||||
m_imgui->text(_(L("Minimal points distance: ")));
|
||||
ImGui::SameLine();
|
||||
bool value_changed = ImGui::InputDouble("mm", &m_minimal_point_distance, 0.0f, 0.0f, "%.2f");
|
||||
m_imgui->text(_(L("Support points density: ")));
|
||||
ImGui::SameLine();
|
||||
value_changed |= ImGui::InputDouble("%", &m_density, 0.0f, 0.0f, "%.f");*/
|
||||
|
||||
bool generate = m_imgui->button(_(L("Auto-generate points [A]")));
|
||||
|
||||
if (generate)
|
||||
auto_generate();
|
||||
|
||||
m_imgui->text("");
|
||||
m_imgui->text("");
|
||||
if (m_imgui->button(_(L("Manual editing [M]"))))
|
||||
switch_to_editing_mode();
|
||||
}
|
||||
|
||||
m_imgui->end();
|
||||
|
||||
if (remove_all_clicked) {
|
||||
delete_current_grabber(true);
|
||||
if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode
|
||||
m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode);
|
||||
force_refresh = true;
|
||||
}
|
||||
m_old_editing_state = m_editing_mode;
|
||||
|
||||
if (remove_selected) {
|
||||
force_refresh = false;
|
||||
m_parent.reload_scene(true);
|
||||
delete_selected_points();
|
||||
if (first_run) {
|
||||
first_run = false;
|
||||
goto RENDER_AGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
if (remove_all_clicked || generate) {
|
||||
if (force_refresh)
|
||||
m_parent.reload_scene(true);
|
||||
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_IMGUI
|
||||
|
||||
bool GLGizmoSlaSupports::on_is_activable(const GLCanvas3D::Selection& selection) const
|
||||
{
|
||||
return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA)
|
||||
&& selection.is_from_single_instance();
|
||||
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA
|
||||
|| !selection.is_from_single_instance())
|
||||
return false;
|
||||
|
||||
// Check that none of the selected volumes is outside.
|
||||
const GLCanvas3D::Selection::IndicesList& list = selection.get_volume_idxs();
|
||||
for (const auto& idx : list)
|
||||
if (selection.get_volume(idx)->is_outside)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLGizmoSlaSupports::on_is_selectable() const
|
||||
|
|
@ -2239,6 +2376,145 @@ std::string GLGizmoSlaSupports::on_get_name() const
|
|||
return L("SLA Support Points [L]");
|
||||
}
|
||||
|
||||
void GLGizmoSlaSupports::on_set_state()
|
||||
{
|
||||
if (m_state == On) {
|
||||
if (is_mesh_update_necessary())
|
||||
update_mesh();
|
||||
|
||||
m_parent.toggle_model_objects_visibility(false);
|
||||
if (m_model_object)
|
||||
m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
|
||||
}
|
||||
if (m_state == Off) {
|
||||
if (m_old_state != Off && m_model_object) { // the gizmo was just turned Off
|
||||
|
||||
if (m_unsaved_changes) {
|
||||
wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L("Do you want to save your manually edited support points ?\n")),
|
||||
_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_YES)
|
||||
editing_mode_apply_changes();
|
||||
else
|
||||
editing_mode_discard_changes();
|
||||
}
|
||||
|
||||
m_parent.toggle_model_objects_visibility(true);
|
||||
m_editing_mode = false; // so it is not active next time the gizmo opens
|
||||
}
|
||||
}
|
||||
m_old_state = m_state;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::on_start_dragging(const GLCanvas3D::Selection& selection)
|
||||
{
|
||||
if (m_hover_id != -1) {
|
||||
select_point(NoPoints);
|
||||
select_point(m_hover_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::select_point(int i)
|
||||
{
|
||||
if (i == AllPoints || i == NoPoints) {
|
||||
for (auto& point_and_selection : m_editing_mode_cache)
|
||||
point_and_selection.second = ( i == AllPoints );
|
||||
m_selection_empty = (i == NoPoints);
|
||||
}
|
||||
else {
|
||||
m_editing_mode_cache[i].second = true;
|
||||
m_selection_empty = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::editing_mode_discard_changes()
|
||||
{
|
||||
m_editing_mode_cache.clear();
|
||||
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
|
||||
m_editing_mode_cache.push_back(std::make_pair(point, false));
|
||||
m_editing_mode = false;
|
||||
m_unsaved_changes = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::editing_mode_apply_changes()
|
||||
{
|
||||
// If there are no changes, don't touch the front-end. The data in the cache could have been
|
||||
// taken from the backend and copying them to ModelObject would needlessly invalidate them.
|
||||
if (m_unsaved_changes) {
|
||||
m_model_object->sla_support_points.clear();
|
||||
for (const std::pair<sla::SupportPoint, bool>& point_and_selection : m_editing_mode_cache)
|
||||
m_model_object->sla_support_points.push_back(point_and_selection.first);
|
||||
}
|
||||
m_editing_mode = false;
|
||||
m_unsaved_changes = false;
|
||||
|
||||
// Recalculate support structures once the editing mode is left.
|
||||
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
wxGetApp().plater()->reslice_SLA_supports(*m_model_object);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::editing_mode_reload_cache()
|
||||
{
|
||||
m_editing_mode_cache.clear();
|
||||
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
|
||||
m_editing_mode_cache.push_back(std::make_pair(point, false));
|
||||
m_unsaved_changes = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::get_data_from_backend()
|
||||
{
|
||||
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
|
||||
if (po->model_object()->id() == m_model_object->id() && po->is_step_done(slaposSupportPoints)) {
|
||||
const std::vector<sla::SupportPoint>& points = po->get_support_points();
|
||||
auto mat = po->trafo().inverse().cast<float>();
|
||||
for (unsigned int i=0; i<points.size();++i)
|
||||
m_editing_mode_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island), false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_unsaved_changes = false;
|
||||
|
||||
// We don't copy the data into ModelObject, as this would stop the background processing.
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::auto_generate()
|
||||
{
|
||||
wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L(
|
||||
"Autogeneration will erase all manually edited points.\n\n"
|
||||
"Are you sure you want to do it?\n"
|
||||
)), _(L("Warning")), wxICON_WARNING | wxYES | wxNO);
|
||||
|
||||
if (m_model_object->sla_support_points.empty() || dlg.ShowModal() == wxID_YES) {
|
||||
m_model_object->sla_support_points.clear();
|
||||
m_editing_mode_cache.clear();
|
||||
wxGetApp().plater()->reslice_SLA_supports(*m_model_object);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::switch_to_editing_mode()
|
||||
{
|
||||
editing_mode_reload_cache();
|
||||
m_editing_mode = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// GLGizmoCut
|
||||
|
|
|
|||
|
|
@ -403,7 +403,7 @@ private:
|
|||
|
||||
// This holds information to decide whether recalculation is necessary:
|
||||
std::vector<Transform3d> m_volumes_matrices;
|
||||
std::vector<ModelVolume::Type> m_volumes_types;
|
||||
std::vector<ModelVolumeType> m_volumes_types;
|
||||
Vec3d m_first_instance_scale;
|
||||
Vec3d m_first_instance_mirror;
|
||||
|
||||
|
|
@ -437,32 +437,26 @@ protected:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class GLGizmoSlaSupports : public GLGizmoBase
|
||||
{
|
||||
private:
|
||||
ModelObject* m_model_object = nullptr;
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
ModelObject* m_old_model_object = nullptr;
|
||||
int m_active_instance = -1;
|
||||
int m_old_instance_id = -1;
|
||||
#else
|
||||
Transform3d m_instance_matrix;
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
Vec3f unproject_on_mesh(const Vec2d& mouse_pos);
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
GLUquadricObj* m_quadric;
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
const float RenderPointScale = 1.f;
|
||||
|
||||
GLUquadricObj* m_quadric;
|
||||
Eigen::MatrixXf m_V; // vertices
|
||||
Eigen::MatrixXi m_F; // facets indices
|
||||
igl::AABB<Eigen::MatrixXf,3> m_AABB;
|
||||
|
||||
struct SourceDataSummary {
|
||||
#if !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
BoundingBoxf3 bounding_box;
|
||||
Transform3d matrix;
|
||||
#endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
Vec3d mesh_first_point;
|
||||
Geometry::Transformation transformation;
|
||||
};
|
||||
|
||||
// This holds information to decide whether recalculation is necessary:
|
||||
|
|
@ -472,14 +466,10 @@ private:
|
|||
|
||||
public:
|
||||
explicit GLGizmoSlaSupports(GLCanvas3D& parent);
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
virtual ~GLGizmoSlaSupports();
|
||||
void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection);
|
||||
#else
|
||||
void set_model_object_ptr(ModelObject* model_object);
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void clicked_on_object(const Vec2d& mouse_position);
|
||||
void delete_current_grabber(bool delete_all);
|
||||
bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down);
|
||||
void delete_selected_points();
|
||||
|
||||
private:
|
||||
bool on_init();
|
||||
|
|
@ -487,11 +477,8 @@ private:
|
|||
virtual void on_render(const GLCanvas3D::Selection& selection) const;
|
||||
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
|
||||
|
||||
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void render_grabbers(const GLCanvas3D::Selection& selection, bool picking = false) const;
|
||||
#else
|
||||
void render_grabbers(bool picking = false) const;
|
||||
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
|
||||
void render_selection_rectangle() const;
|
||||
void render_points(const GLCanvas3D::Selection& selection, bool picking = false) const;
|
||||
bool is_mesh_update_necessary() const;
|
||||
void update_mesh();
|
||||
|
||||
|
|
@ -501,12 +488,42 @@ private:
|
|||
mutable GLTexture m_reset_texture;
|
||||
#endif // not ENABLE_IMGUI
|
||||
|
||||
bool m_lock_unique_islands = false;
|
||||
bool m_editing_mode = false;
|
||||
bool m_old_editing_state = false;
|
||||
float m_new_point_head_diameter = 0.4f;
|
||||
double m_minimal_point_distance = 20.;
|
||||
double m_density = 100.;
|
||||
std::vector<std::pair<sla::SupportPoint, bool>> m_editing_mode_cache; // a support point and whether it is currently selected
|
||||
|
||||
bool m_selection_rectangle_active = false;
|
||||
Vec2d m_selection_rectangle_start_corner;
|
||||
Vec2d m_selection_rectangle_end_corner;
|
||||
bool m_ignore_up_event = false;
|
||||
bool m_combo_box_open = false;
|
||||
bool m_unsaved_changes = false;
|
||||
bool m_selection_empty = true;
|
||||
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
|
||||
int m_canvas_width;
|
||||
int m_canvas_height;
|
||||
|
||||
// Methods that do the model_object and editing cache synchronization,
|
||||
// editing mode selection, etc:
|
||||
enum {
|
||||
AllPoints = -2,
|
||||
NoPoints,
|
||||
};
|
||||
void select_point(int i);
|
||||
void editing_mode_apply_changes();
|
||||
void editing_mode_discard_changes();
|
||||
void editing_mode_reload_cache();
|
||||
void get_data_from_backend();
|
||||
void auto_generate();
|
||||
void switch_to_editing_mode();
|
||||
|
||||
protected:
|
||||
void on_set_state() override {
|
||||
if (m_state == On && is_mesh_update_necessary()) {
|
||||
update_mesh();
|
||||
}
|
||||
}
|
||||
void on_set_state() override;
|
||||
void on_start_dragging(const GLCanvas3D::Selection& selection) override;
|
||||
|
||||
#if ENABLE_IMGUI
|
||||
virtual void on_render_input_window(float x, float y, const GLCanvas3D::Selection& selection) override;
|
||||
|
|
|
|||
|
|
@ -253,4 +253,91 @@ sub SetMatrix
|
|||
}
|
||||
*/
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
Shader::Shader()
|
||||
: m_shader(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Shader::~Shader()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
bool Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
|
||||
{
|
||||
if (is_initialized())
|
||||
return true;
|
||||
|
||||
m_shader = new GLShader();
|
||||
if (m_shader != nullptr)
|
||||
{
|
||||
if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str()))
|
||||
{
|
||||
std::cout << "Compilaton of shader failed:" << std::endl;
|
||||
std::cout << m_shader->last_error << std::endl;
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Shader::is_initialized() const
|
||||
{
|
||||
return (m_shader != nullptr);
|
||||
}
|
||||
|
||||
bool Shader::start_using() const
|
||||
{
|
||||
if (is_initialized())
|
||||
{
|
||||
m_shader->enable();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void Shader::stop_using() const
|
||||
{
|
||||
if (m_shader != nullptr)
|
||||
m_shader->disable();
|
||||
}
|
||||
|
||||
void Shader::set_uniform(const std::string& name, float value) const
|
||||
{
|
||||
if (m_shader != nullptr)
|
||||
m_shader->set_uniform(name.c_str(), value);
|
||||
}
|
||||
|
||||
void Shader::set_uniform(const std::string& name, const float* matrix) const
|
||||
{
|
||||
if (m_shader != nullptr)
|
||||
m_shader->set_uniform(name.c_str(), matrix);
|
||||
}
|
||||
|
||||
void Shader::set_uniform(const std::string& name, bool value) const
|
||||
{
|
||||
if (m_shader != nullptr)
|
||||
m_shader->set_uniform(name.c_str(), value);
|
||||
}
|
||||
|
||||
unsigned int Shader::get_shader_program_id() const
|
||||
{
|
||||
return (m_shader != nullptr) ? m_shader->shader_program_id : 0;
|
||||
}
|
||||
|
||||
void Shader::reset()
|
||||
{
|
||||
if (m_shader != nullptr)
|
||||
{
|
||||
m_shader->release();
|
||||
delete m_shader;
|
||||
m_shader = nullptr;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -36,6 +36,34 @@ public:
|
|||
std::string last_error;
|
||||
};
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
class Shader
|
||||
{
|
||||
GLShader* m_shader;
|
||||
|
||||
public:
|
||||
Shader();
|
||||
~Shader();
|
||||
|
||||
bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename);
|
||||
|
||||
bool is_initialized() const;
|
||||
|
||||
bool start_using() const;
|
||||
void stop_using() const;
|
||||
|
||||
void set_uniform(const std::string& name, float value) const;
|
||||
void set_uniform(const std::string& name, const float* matrix) const;
|
||||
void set_uniform(const std::string& name, bool value) const;
|
||||
|
||||
const GLShader* get_shader() const { return m_shader; }
|
||||
unsigned int get_shader_program_id() const;
|
||||
|
||||
private:
|
||||
void reset();
|
||||
};
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
}
|
||||
|
||||
#endif /* slic3r_GLShader_hpp_ */
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "libslic3r/libslic3r.h"
|
||||
#include "GLTexture.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
|
@ -5,10 +6,20 @@
|
|||
#include <wx/image.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
#define NANOSVG_IMPLEMENTATION
|
||||
#include "nanosvg/nanosvg.h"
|
||||
#define NANOSVGRAST_IMPLEMENTATION
|
||||
#include "nanosvg/nanosvgrast.h"
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
|
@ -27,7 +38,34 @@ GLTexture::~GLTexture()
|
|||
reset();
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmaps)
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps)
|
||||
{
|
||||
reset();
|
||||
|
||||
if (!boost::filesystem::exists(filename))
|
||||
return false;
|
||||
|
||||
if (boost::algorithm::iends_with(filename, ".png"))
|
||||
return load_from_png(filename, use_mipmaps);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps, unsigned int max_size_px)
|
||||
{
|
||||
reset();
|
||||
|
||||
if (!boost::filesystem::exists(filename))
|
||||
return false;
|
||||
|
||||
if (boost::algorithm::iends_with(filename, ".svg"))
|
||||
return load_from_svg(filename, use_mipmaps, max_size_px);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps)
|
||||
{
|
||||
reset();
|
||||
|
||||
|
|
@ -78,10 +116,10 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap
|
|||
::glGenTextures(1, &m_id);
|
||||
::glBindTexture(GL_TEXTURE_2D, m_id);
|
||||
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
|
||||
if (generate_mipmaps)
|
||||
if (use_mipmaps)
|
||||
{
|
||||
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
|
||||
unsigned int levels_count = _generate_mipmaps(image);
|
||||
unsigned int levels_count = generate_mipmaps(image);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + levels_count);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
|
|
@ -97,6 +135,7 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap
|
|||
m_source = filename;
|
||||
return true;
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
void GLTexture::reset()
|
||||
{
|
||||
|
|
@ -109,26 +148,6 @@ void GLTexture::reset()
|
|||
m_source = "";
|
||||
}
|
||||
|
||||
unsigned int GLTexture::get_id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
int GLTexture::get_width() const
|
||||
{
|
||||
return m_width;
|
||||
}
|
||||
|
||||
int GLTexture::get_height() const
|
||||
{
|
||||
return m_height;
|
||||
}
|
||||
|
||||
const std::string& GLTexture::get_source() const
|
||||
{
|
||||
return m_source;
|
||||
}
|
||||
|
||||
void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top)
|
||||
{
|
||||
render_sub_texture(tex_id, left, right, bottom, top, FullTextureUVs);
|
||||
|
|
@ -157,7 +176,7 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
|
|||
::glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
unsigned int GLTexture::_generate_mipmaps(wxImage& image)
|
||||
unsigned int GLTexture::generate_mipmaps(wxImage& image)
|
||||
{
|
||||
int w = image.GetWidth();
|
||||
int h = image.GetHeight();
|
||||
|
|
@ -195,5 +214,152 @@ unsigned int GLTexture::_generate_mipmaps(wxImage& image)
|
|||
return (unsigned int)level;
|
||||
}
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps)
|
||||
{
|
||||
// Load a PNG with an alpha channel.
|
||||
wxImage image;
|
||||
if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG))
|
||||
{
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_width = image.GetWidth();
|
||||
m_height = image.GetHeight();
|
||||
int n_pixels = m_width * m_height;
|
||||
|
||||
if (n_pixels <= 0)
|
||||
{
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get RGB & alpha raw data from wxImage, pack them into an array.
|
||||
unsigned char* img_rgb = image.GetData();
|
||||
if (img_rgb == nullptr)
|
||||
{
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char* img_alpha = image.GetAlpha();
|
||||
|
||||
std::vector<unsigned char> data(n_pixels * 4, 0);
|
||||
for (int i = 0; i < n_pixels; ++i)
|
||||
{
|
||||
int data_id = i * 4;
|
||||
int img_id = i * 3;
|
||||
data[data_id + 0] = img_rgb[img_id + 0];
|
||||
data[data_id + 1] = img_rgb[img_id + 1];
|
||||
data[data_id + 2] = img_rgb[img_id + 2];
|
||||
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
|
||||
}
|
||||
|
||||
// sends data to gpu
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
::glGenTextures(1, &m_id);
|
||||
::glBindTexture(GL_TEXTURE_2D, m_id);
|
||||
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
|
||||
if (use_mipmaps)
|
||||
{
|
||||
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
|
||||
unsigned int levels_count = generate_mipmaps(image);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + levels_count);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
else
|
||||
{
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
}
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
::glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
m_source = filename;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px)
|
||||
{
|
||||
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
|
||||
if (image == nullptr)
|
||||
{
|
||||
// printf("Could not open SVG image.\n");
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
float scale = (float)max_size_px / std::max(image->width, image->height);
|
||||
|
||||
m_width = (int)(scale * image->width);
|
||||
m_height = (int)(scale * image->height);
|
||||
int n_pixels = m_width * m_height;
|
||||
|
||||
if (n_pixels <= 0)
|
||||
{
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
NSVGrasterizer* rast = nsvgCreateRasterizer();
|
||||
if (rast == nullptr)
|
||||
{
|
||||
// printf("Could not init rasterizer.\n");
|
||||
nsvgDelete(image);
|
||||
reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
// creates the temporary buffer only once, with max size, and reuse it for all the levels, if generating mipmaps
|
||||
std::vector<unsigned char> data(n_pixels * 4, 0);
|
||||
nsvgRasterize(rast, image, 0, 0, scale, data.data(), m_width, m_height, m_width * 4);
|
||||
|
||||
// sends data to gpu
|
||||
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
::glGenTextures(1, &m_id);
|
||||
::glBindTexture(GL_TEXTURE_2D, m_id);
|
||||
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
|
||||
if (use_mipmaps)
|
||||
{
|
||||
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
|
||||
int lod_w = m_width;
|
||||
int lod_h = m_height;
|
||||
GLint level = 0;
|
||||
while ((lod_w > 1) || (lod_h > 1))
|
||||
{
|
||||
++level;
|
||||
|
||||
lod_w = std::max(lod_w / 2, 1);
|
||||
lod_h = std::max(lod_h / 2, 1);
|
||||
scale /= 2.0f;
|
||||
|
||||
nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4);
|
||||
::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
|
||||
}
|
||||
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + level);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
else
|
||||
{
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
}
|
||||
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
::glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
m_source = filename;
|
||||
|
||||
nsvgDeleteRasterizer(rast);
|
||||
nsvgDelete(image);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -37,20 +37,28 @@ namespace GUI {
|
|||
GLTexture();
|
||||
virtual ~GLTexture();
|
||||
|
||||
bool load_from_file(const std::string& filename, bool generate_mipmaps);
|
||||
bool load_from_file(const std::string& filename, bool use_mipmaps);
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
bool load_from_svg_file(const std::string& filename, bool use_mipmaps, unsigned int max_size_px);
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
void reset();
|
||||
|
||||
unsigned int get_id() const;
|
||||
int get_width() const;
|
||||
int get_height() const;
|
||||
unsigned int get_id() const { return m_id; }
|
||||
int get_width() const { return m_width; }
|
||||
int get_height() const { return m_height; }
|
||||
|
||||
const std::string& get_source() const;
|
||||
const std::string& get_source() const { return m_source; }
|
||||
|
||||
static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top);
|
||||
static void render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const Quad_UVs& uvs);
|
||||
|
||||
protected:
|
||||
unsigned int _generate_mipmaps(wxImage& image);
|
||||
unsigned int generate_mipmaps(wxImage& image);
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
private:
|
||||
bool load_from_png(const std::string& filename, bool use_mipmaps);
|
||||
bool load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px);
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
|
|
|||
|
|
@ -211,8 +211,9 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
|
|||
}
|
||||
break;
|
||||
case coEnum:{
|
||||
if (opt_key.compare("external_fill_pattern") == 0 ||
|
||||
opt_key.compare("fill_pattern") == 0)
|
||||
if (opt_key == "top_fill_pattern" ||
|
||||
opt_key == "bottom_fill_pattern" ||
|
||||
opt_key == "fill_pattern")
|
||||
config.set_key_value(opt_key, new ConfigOptionEnum<InfillPattern>(boost::any_cast<InfillPattern>(value)));
|
||||
else if (opt_key.compare("gcode_flavor") == 0)
|
||||
config.set_key_value(opt_key, new ConfigOptionEnum<GCodeFlavor>(boost::any_cast<GCodeFlavor>(value)));
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
#include "GUI_ObjectManipulation.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <wx/stdpaths.h>
|
||||
#include <wx/imagpng.h>
|
||||
|
|
@ -76,6 +78,7 @@ IMPLEMENT_APP(GUI_App)
|
|||
|
||||
GUI_App::GUI_App()
|
||||
: wxApp()
|
||||
, m_em_unit(10)
|
||||
#if ENABLE_IMGUI
|
||||
, m_imgui(new ImGuiWrapper())
|
||||
#endif // ENABLE_IMGUI
|
||||
|
|
@ -125,6 +128,10 @@ bool GUI_App::OnInit()
|
|||
app_config->save();
|
||||
|
||||
preset_updater = new PresetUpdater();
|
||||
Bind(EVT_SLIC3R_VERSION_ONLINE, [this](const wxCommandEvent &evt) {
|
||||
app_config->set("version_online", into_u8(evt.GetString()));
|
||||
app_config->save();
|
||||
});
|
||||
|
||||
load_language();
|
||||
|
||||
|
|
@ -154,15 +161,15 @@ bool GUI_App::OnInit()
|
|||
|
||||
Bind(wxEVT_IDLE, [this](wxIdleEvent& event)
|
||||
{
|
||||
if (app_config->dirty())
|
||||
if (app_config->dirty() && app_config->get("autosave") == "1")
|
||||
app_config->save();
|
||||
|
||||
// ! Temporary workaround for the correct behavior of the Scrolled sidebar panel
|
||||
// Do this "manipulations" only once ( after (re)create of the application )
|
||||
if (plater_ && sidebar().obj_list()->GetMinHeight() > 200)
|
||||
if (plater_ && sidebar().obj_list()->GetMinHeight() > 15 * wxGetApp().em_unit())
|
||||
{
|
||||
wxWindowUpdateLocker noUpdates_sidebar(&sidebar());
|
||||
sidebar().obj_list()->SetMinSize(wxSize(-1, 200));
|
||||
sidebar().obj_list()->SetMinSize(wxSize(-1, 15 * wxGetApp().em_unit()));
|
||||
|
||||
// !!! to correct later layouts
|
||||
update_mode(); // update view mode after fix of the object_list size
|
||||
|
|
@ -181,7 +188,6 @@ bool GUI_App::OnInit()
|
|||
mainframe->Close();
|
||||
} catch (const std::exception &ex) {
|
||||
show_error(nullptr, ex.what());
|
||||
mainframe->Close();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -246,6 +252,7 @@ void GUI_App::init_fonts()
|
|||
{
|
||||
m_small_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
m_bold_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold();
|
||||
|
||||
#ifdef __WXMAC__
|
||||
m_small_font.SetPointSize(11);
|
||||
m_bold_font.SetPointSize(13);
|
||||
|
|
@ -407,6 +414,7 @@ bool GUI_App::select_language( wxArrayString & names,
|
|||
//FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only.
|
||||
wxSetlocale(LC_NUMERIC, "C");
|
||||
Preset::update_suffix_modified();
|
||||
m_imgui->set_language(m_wxLocale->GetCanonicalName().ToUTF8().data());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -435,6 +443,7 @@ bool GUI_App::load_language()
|
|||
//FIXME This is a temporary workaround, the correct solution is to switch to "C" locale during file import / export only.
|
||||
wxSetlocale(LC_NUMERIC, "C");
|
||||
Preset::update_suffix_modified();
|
||||
m_imgui->set_language(m_wxLocale->GetCanonicalName().ToUTF8().data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -562,7 +571,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("Simple")), _(L("Simple View Mode")));
|
||||
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _(L("Advanced")), _(L("Advanced View Mode")));
|
||||
mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _(L("Expert")), _(L("Expert View Mode")));
|
||||
mode_menu->Check(config_id_base + ConfigMenuModeSimple + get_mode(), true);
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Check(get_mode() == comSimple); }, config_id_base + ConfigMenuModeSimple);
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Check(get_mode() == comAdvanced); }, config_id_base + ConfigMenuModeAdvanced);
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Check(get_mode() == comExpert); }, config_id_base + ConfigMenuModeExpert);
|
||||
|
||||
local_menu->AppendSubMenu(mode_menu, _(L("Mode")), _(L("Slic3r View Mode")));
|
||||
local_menu->AppendSeparator();
|
||||
local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application &Language")));
|
||||
|
|
@ -681,6 +693,23 @@ void GUI_App::load_current_presets()
|
|||
}
|
||||
}
|
||||
|
||||
bool GUI_App::OnExceptionInMainLoop()
|
||||
{
|
||||
try {
|
||||
throw;
|
||||
} catch (const std::exception &ex) {
|
||||
const std::string error = (boost::format("Uncaught exception: %1%") % ex.what()).str();
|
||||
BOOST_LOG_TRIVIAL(error) << error;
|
||||
show_error(nullptr, from_u8(error));
|
||||
} catch (...) {
|
||||
const char *error = "Uncaught exception: Unknown error";
|
||||
BOOST_LOG_TRIVIAL(error) << error;
|
||||
show_error(nullptr, from_u8(error));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
// wxWidgets override to get an event on open files.
|
||||
void GUI_App::MacOpenFiles(const wxArrayString &fileNames)
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ class GUI_App : public wxApp
|
|||
wxFont m_small_font;
|
||||
wxFont m_bold_font;
|
||||
|
||||
size_t m_em_unit; // width of a "m"-symbol in pixels for current system font
|
||||
// Note: for 100% Scale m_em_unit = 10 -> it's a good enough coefficient for a size setting of controls
|
||||
|
||||
wxLocale* m_wxLocale{ nullptr };
|
||||
|
||||
#if ENABLE_IMGUI
|
||||
|
|
@ -108,6 +111,8 @@ public:
|
|||
|
||||
const wxFont& small_font() { return m_small_font; }
|
||||
const wxFont& bold_font() { return m_bold_font; }
|
||||
size_t em_unit() const { return m_em_unit; }
|
||||
void set_em_unit(const size_t em_unit) { m_em_unit = em_unit; }
|
||||
|
||||
void recreate_GUI();
|
||||
void system_info();
|
||||
|
|
@ -137,6 +142,8 @@ public:
|
|||
bool checked_tab(Tab* tab);
|
||||
void load_current_presets();
|
||||
|
||||
virtual bool OnExceptionInMainLoop();
|
||||
|
||||
#ifdef __APPLE__
|
||||
// wxWidgets override to get an event on open files.
|
||||
void MacOpenFiles(const wxArrayString &fileNames) override;
|
||||
|
|
|
|||
|
|
@ -45,18 +45,18 @@ ObjectList::ObjectList(wxWindow* parent) :
|
|||
// Fill CATEGORY_ICON
|
||||
{
|
||||
// ptFFF
|
||||
CATEGORY_ICON[L("Layers and Perimeters")] = wxBitmap(from_u8(var("layers.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Infill")] = wxBitmap(from_u8(var("infill.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Support material")] = wxBitmap(from_u8(var("building.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Speed")] = wxBitmap(from_u8(var("time.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Extruders")] = wxBitmap(from_u8(var("funnel.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Extrusion Width")] = wxBitmap(from_u8(var("funnel.png")), wxBITMAP_TYPE_PNG);
|
||||
// CATEGORY_ICON[L("Skirt and brim")] = wxBitmap(from_u8(var("box.png")), wxBITMAP_TYPE_PNG);
|
||||
// CATEGORY_ICON[L("Speed > Acceleration")] = wxBitmap(from_u8(var("time.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Advanced")] = wxBitmap(from_u8(var("wand.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Layers and Perimeters")] = create_scaled_bitmap("layers.png"); // wxBitmap(from_u8(var("layers.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Infill")] = create_scaled_bitmap("infill.png"); // wxBitmap(from_u8(var("infill.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Support material")] = create_scaled_bitmap("building.png"); // wxBitmap(from_u8(var("building.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Speed")] = create_scaled_bitmap("time.png"); // wxBitmap(from_u8(var("time.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel.png"); // wxBitmap(from_u8(var("funnel.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap("funnel.png"); // wxBitmap(from_u8(var("funnel.png")), wxBITMAP_TYPE_PNG);
|
||||
// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("box.png"); // wxBitmap(from_u8(var("box.png")), wxBITMAP_TYPE_PNG);
|
||||
// CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap("time.png"); // wxBitmap(from_u8(var("time.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wand.png"); // wxBitmap(from_u8(var("wand.png")), wxBITMAP_TYPE_PNG);
|
||||
// ptSLA
|
||||
CATEGORY_ICON[L("Supports")] = wxBitmap(from_u8(var("building.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Pad")] = wxBitmap(from_u8(var("brick.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Supports")] = create_scaled_bitmap("building.png"); // wxBitmap(from_u8(var("building.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Pad")] = create_scaled_bitmap("brick.png"); // wxBitmap(from_u8(var("brick.png")), wxBITMAP_TYPE_PNG);
|
||||
}
|
||||
|
||||
// create control
|
||||
|
|
@ -116,7 +116,7 @@ void ObjectList::create_objects_ctrl()
|
|||
SetMinSize(wxSize(-1, 3000)); // #ys_FIXME
|
||||
|
||||
m_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_sizer->Add(this, 1, wxGROW | wxLEFT, 20);
|
||||
m_sizer->Add(this, 1, wxGROW);
|
||||
|
||||
m_objects_model = new PrusaObjectDataViewModel;
|
||||
AssociateModel(m_objects_model);
|
||||
|
|
@ -129,13 +129,13 @@ void ObjectList::create_objects_ctrl()
|
|||
// column 0(Icon+Text) of the view control:
|
||||
// And Icon can be consisting of several bitmaps
|
||||
AppendColumn(new wxDataViewColumn(_(L("Name")), new PrusaBitmapTextRenderer(),
|
||||
0, 200, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
|
||||
0, 20*wxGetApp().em_unit()/*200*/, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
|
||||
|
||||
// column 1 of the view control:
|
||||
AppendColumn(create_objects_list_extruder_column(4));
|
||||
|
||||
// column 2 of the view control:
|
||||
AppendBitmapColumn(" ", 2, wxDATAVIEW_CELL_INERT, 25,
|
||||
AppendBitmapColumn(" ", 2, wxDATAVIEW_CELL_INERT, int(2.5 * wxGetApp().em_unit())/*25*/,
|
||||
wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
|
||||
}
|
||||
|
||||
|
|
@ -218,7 +218,8 @@ wxDataViewColumn* ObjectList::create_objects_list_extruder_column(int extruders_
|
|||
choices.Add(wxString::Format("%d", i));
|
||||
wxDataViewChoiceRenderer *c =
|
||||
new wxDataViewChoiceRenderer(choices, wxDATAVIEW_CELL_EDITABLE, wxALIGN_CENTER_HORIZONTAL);
|
||||
wxDataViewColumn* column = new wxDataViewColumn(_(L("Extruder")), c, 1, 80, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
|
||||
wxDataViewColumn* column = new wxDataViewColumn(_(L("Extruder")), c, 1,
|
||||
8*wxGetApp().em_unit()/*80*/, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE);
|
||||
return column;
|
||||
}
|
||||
|
||||
|
|
@ -334,11 +335,18 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const
|
|||
|
||||
void ObjectList::init_icons()
|
||||
{
|
||||
m_bmp_modifiermesh = wxBitmap(from_u8(var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
|
||||
m_bmp_solidmesh = wxBitmap(from_u8(var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
|
||||
// m_bmp_modifiermesh = wxBitmap(from_u8(var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
|
||||
// m_bmp_solidmesh = wxBitmap(from_u8(var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
|
||||
|
||||
// m_bmp_support_enforcer = wxBitmap(from_u8(var("support_enforcer_.png")), wxBITMAP_TYPE_PNG);
|
||||
// m_bmp_support_blocker = wxBitmap(from_u8(var("support_blocker_.png")), wxBITMAP_TYPE_PNG);
|
||||
|
||||
|
||||
m_bmp_modifiermesh = create_scaled_bitmap("lambda.png");
|
||||
m_bmp_solidmesh = create_scaled_bitmap("object.png");
|
||||
m_bmp_support_enforcer = create_scaled_bitmap("support_enforcer_.png");
|
||||
m_bmp_support_blocker = create_scaled_bitmap("support_blocker_.png");
|
||||
|
||||
m_bmp_support_enforcer = wxBitmap(from_u8(var("support_enforcer_.png")), wxBITMAP_TYPE_PNG);
|
||||
m_bmp_support_blocker = wxBitmap(from_u8(var("support_blocker_.png")), wxBITMAP_TYPE_PNG);
|
||||
|
||||
m_bmp_vector.reserve(4); // bitmaps for different types of parts
|
||||
m_bmp_vector.push_back(&m_bmp_solidmesh); // Add part
|
||||
|
|
@ -348,13 +356,16 @@ void ObjectList::init_icons()
|
|||
m_objects_model->SetVolumeBitmaps(m_bmp_vector);
|
||||
|
||||
// init icon for manifold warning
|
||||
m_bmp_manifold_warning = wxBitmap(from_u8(var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
|
||||
// m_bmp_manifold_warning = wxBitmap(from_u8(var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
|
||||
m_bmp_manifold_warning = create_scaled_bitmap("exclamation_mark_.png");
|
||||
|
||||
// init bitmap for "Split to sub-objects" context menu
|
||||
m_bmp_split = wxBitmap(from_u8(var("split.png")), wxBITMAP_TYPE_PNG);
|
||||
// m_bmp_split = wxBitmap(from_u8(var("split.png")), wxBITMAP_TYPE_PNG);
|
||||
m_bmp_split = create_scaled_bitmap("split.png");
|
||||
|
||||
// init bitmap for "Add Settings" context menu
|
||||
m_bmp_cog = wxBitmap(from_u8(var("cog.png")), wxBITMAP_TYPE_PNG);
|
||||
// m_bmp_cog = wxBitmap(from_u8(var("cog.png")), wxBITMAP_TYPE_PNG);
|
||||
m_bmp_cog = create_scaled_bitmap("cog.png");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -807,7 +818,7 @@ void ObjectList::update_settings_item()
|
|||
}
|
||||
}
|
||||
|
||||
void ObjectList::append_menu_item_add_generic(wxMenuItem* menu, const int type) {
|
||||
void ObjectList::append_menu_item_add_generic(wxMenuItem* menu, const ModelVolumeType type) {
|
||||
auto sub_menu = new wxMenu;
|
||||
|
||||
if (wxGetApp().get_mode() == comExpert) {
|
||||
|
|
@ -816,10 +827,9 @@ void ObjectList::append_menu_item_add_generic(wxMenuItem* menu, const int type)
|
|||
sub_menu->AppendSeparator();
|
||||
}
|
||||
|
||||
std::vector<std::string> menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") };
|
||||
for (auto& item : menu_items) {
|
||||
for (auto& item : { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }) {
|
||||
append_menu_item(sub_menu, wxID_ANY, _(item), "",
|
||||
[this, type, item](wxCommandEvent&) { load_generic_subobject(_(item).ToUTF8().data(), type); }, "", menu->GetMenu());
|
||||
[this, type, item](wxCommandEvent&) { load_generic_subobject(item, type); }, "", menu->GetMenu());
|
||||
}
|
||||
|
||||
menu->SetSubMenu(sub_menu);
|
||||
|
|
@ -828,10 +838,10 @@ void ObjectList::append_menu_item_add_generic(wxMenuItem* menu, const int type)
|
|||
void ObjectList::append_menu_items_add_volume(wxMenu* menu)
|
||||
{
|
||||
// Note: id accords to type of the sub-object, so sequence of the menu items is important
|
||||
std::vector<std::string> menu_object_types_items = {L("Add part"), // ~ModelVolume::MODEL_PART
|
||||
L("Add modifier"), // ~ModelVolume::PARAMETER_MODIFIER
|
||||
L("Add support enforcer"), // ~ModelVolume::SUPPORT_ENFORCER
|
||||
L("Add support blocker") }; // ~ModelVolume::SUPPORT_BLOCKER
|
||||
std::vector<std::string> menu_object_types_items = {L("Add part"), // ~ModelVolumeType::MODEL_PART
|
||||
L("Add modifier"), // ~ModelVolumeType::PARAMETER_MODIFIER
|
||||
L("Add support enforcer"), // ~ModelVolumeType::SUPPORT_ENFORCER
|
||||
L("Add support blocker") }; // ~ModelVolumeType::SUPPORT_BLOCKER
|
||||
|
||||
// Update "add" items(delete old & create new) settings popupmenu
|
||||
for (auto& item : menu_object_types_items){
|
||||
|
|
@ -845,15 +855,15 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu)
|
|||
if (mode < comExpert)
|
||||
{
|
||||
append_menu_item(menu, wxID_ANY, _(L("Add part")), "",
|
||||
[this](wxCommandEvent&) { load_subobject(ModelVolume::MODEL_PART); }, *m_bmp_vector[ModelVolume::MODEL_PART]);
|
||||
[this](wxCommandEvent&) { load_subobject(ModelVolumeType::MODEL_PART); }, *m_bmp_vector[int(ModelVolumeType::MODEL_PART)]);
|
||||
}
|
||||
if (mode == comSimple) {
|
||||
append_menu_item(menu, wxID_ANY, _(L("Add support enforcer")), "",
|
||||
[this](wxCommandEvent&) { load_generic_subobject(_(L("Box")).ToUTF8().data(), ModelVolume::SUPPORT_ENFORCER); },
|
||||
*m_bmp_vector[ModelVolume::SUPPORT_ENFORCER]);
|
||||
[this](wxCommandEvent&) { load_generic_subobject(L("Box"), ModelVolumeType::SUPPORT_ENFORCER); },
|
||||
*m_bmp_vector[int(ModelVolumeType::SUPPORT_ENFORCER)]);
|
||||
append_menu_item(menu, wxID_ANY, _(L("Add support blocker")), "",
|
||||
[this](wxCommandEvent&) { load_generic_subobject(_(L("Box")).ToUTF8().data(), ModelVolume::SUPPORT_BLOCKER); },
|
||||
*m_bmp_vector[ModelVolume::SUPPORT_BLOCKER]);
|
||||
[this](wxCommandEvent&) { load_generic_subobject(L("Box"), ModelVolumeType::SUPPORT_BLOCKER); },
|
||||
*m_bmp_vector[int(ModelVolumeType::SUPPORT_BLOCKER)]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -864,7 +874,7 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu)
|
|||
|
||||
auto menu_item = new wxMenuItem(menu, wxID_ANY, _(item));
|
||||
menu_item->SetBitmap(*m_bmp_vector[type]);
|
||||
append_menu_item_add_generic(menu_item, type);
|
||||
append_menu_item_add_generic(menu_item, ModelVolumeType(type));
|
||||
|
||||
menu->Append(menu_item);
|
||||
}
|
||||
|
|
@ -913,7 +923,7 @@ wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
|
|||
menu->DestroySeparators(); // delete old separators
|
||||
|
||||
const auto sel_vol = get_selected_model_volume();
|
||||
if (sel_vol && sel_vol->type() >= ModelVolume::SUPPORT_ENFORCER)
|
||||
if (sel_vol && sel_vol->type() >= ModelVolumeType::SUPPORT_ENFORCER)
|
||||
return nullptr;
|
||||
|
||||
const ConfigOptionMode mode = wxGetApp().get_mode();
|
||||
|
|
@ -937,7 +947,7 @@ wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
|
|||
menu_item->SetBitmap(m_bmp_cog);
|
||||
|
||||
// const auto sel_vol = get_selected_model_volume();
|
||||
// if (sel_vol && sel_vol->type() >= ModelVolume::SUPPORT_ENFORCER)
|
||||
// if (sel_vol && sel_vol->type() >= ModelVolumeType::SUPPORT_ENFORCER)
|
||||
// menu_item->Enable(false);
|
||||
// else
|
||||
menu_item->SetSubMenu(create_settings_popupmenu(menu));
|
||||
|
|
@ -1092,7 +1102,7 @@ void ObjectList::update_opt_keys(t_config_option_keys& opt_keys)
|
|||
opt_keys.erase(opt_keys.begin() + i);
|
||||
}
|
||||
|
||||
void ObjectList::load_subobject(int type)
|
||||
void ObjectList::load_subobject(ModelVolumeType type)
|
||||
{
|
||||
auto item = GetSelection();
|
||||
if (!item || m_objects_model->GetParent(item) != wxDataViewItem(0))
|
||||
|
|
@ -1115,7 +1125,7 @@ void ObjectList::load_subobject(int type)
|
|||
|
||||
void ObjectList::load_part( ModelObject* model_object,
|
||||
wxArrayString& part_names,
|
||||
int type)
|
||||
ModelVolumeType type)
|
||||
{
|
||||
wxWindow* parent = wxGetApp().tab_panel()->GetPage(0);
|
||||
|
||||
|
|
@ -1148,7 +1158,7 @@ void ObjectList::load_part( ModelObject* model_object,
|
|||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
volume->translate(delta);
|
||||
auto new_volume = model_object->add_volume(*volume);
|
||||
new_volume->set_type(static_cast<ModelVolume::Type>(type));
|
||||
new_volume->set_type(type);
|
||||
new_volume->name = boost::filesystem::path(input_file).filename().string();
|
||||
|
||||
part_names.Add(from_u8(new_volume->name));
|
||||
|
|
@ -1163,28 +1173,28 @@ void ObjectList::load_part( ModelObject* model_object,
|
|||
|
||||
}
|
||||
|
||||
void ObjectList::load_generic_subobject(const std::string& type_name, const int type)
|
||||
void ObjectList::load_generic_subobject(const std::string& type_name, const ModelVolumeType type)
|
||||
{
|
||||
const auto obj_idx = get_selected_obj_idx();
|
||||
if (obj_idx < 0) return;
|
||||
|
||||
const std::string name = "lambda-" + type_name;
|
||||
const wxString name = _(L("Generic")) + "-" + _(type_name);
|
||||
TriangleMesh mesh;
|
||||
|
||||
auto& bed_shape = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionPoints>("bed_shape")->values;
|
||||
const auto& sz = BoundingBoxf(bed_shape).size();
|
||||
const auto side = 0.1 * std::max(sz(0), sz(1));
|
||||
|
||||
if (type_name == _("Box")) {
|
||||
if (type_name == "Box") {
|
||||
mesh = make_cube(side, side, side);
|
||||
// box sets the base coordinate at 0, 0, move to center of plate
|
||||
mesh.translate(-side * 0.5, -side * 0.5, 0);
|
||||
}
|
||||
else if (type_name == _("Cylinder"))
|
||||
else if (type_name == "Cylinder")
|
||||
mesh = make_cylinder(0.5*side, side);
|
||||
else if (type_name == _("Sphere"))
|
||||
else if (type_name == "Sphere")
|
||||
mesh = make_sphere(0.5*side, PI/18);
|
||||
else if (type_name == _("Slab")) {
|
||||
else if (type_name == "Slab") {
|
||||
const auto& size = (*m_objects)[obj_idx]->bounding_box().size();
|
||||
mesh = make_cube(size(0)*1.5, size(1)*1.5, size(2)*0.5);
|
||||
// box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
|
||||
|
|
@ -1193,7 +1203,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const int
|
|||
mesh.repair();
|
||||
|
||||
auto new_volume = (*m_objects)[obj_idx]->add_volume(mesh);
|
||||
new_volume->set_type(static_cast<ModelVolume::Type>(type));
|
||||
new_volume->set_type(type);
|
||||
|
||||
#if !ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
new_volume->set_offset(Vec3d(0.0, 0.0, (*m_objects)[obj_idx]->origin_translation(2) - mesh.stl.stats.min(2)));
|
||||
|
|
@ -1220,7 +1230,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const int
|
|||
}
|
||||
#endif // ENABLE_GENERIC_SUBPARTS_PLACEMENT
|
||||
|
||||
new_volume->name = name;
|
||||
new_volume->name = into_u8(name);
|
||||
// set a default extruder value, since user can't add it manually
|
||||
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
|
||||
|
|
@ -1228,7 +1238,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const int
|
|||
parts_changed(obj_idx);
|
||||
|
||||
const auto object_item = m_objects_model->GetTopParent(GetSelection());
|
||||
select_item(m_objects_model->AddVolumeChild(object_item, from_u8(name), type));
|
||||
select_item(m_objects_model->AddVolumeChild(object_item, name, type));
|
||||
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
|
||||
selection_changed();
|
||||
#endif //no __WXOSX__ //__WXMSW__
|
||||
|
|
@ -1360,7 +1370,7 @@ void ObjectList::split()
|
|||
for (auto id = 0; id < model_object->volumes.size(); id++) {
|
||||
const auto vol_item = m_objects_model->AddVolumeChild(parent, from_u8(model_object->volumes[id]->name),
|
||||
model_object->volumes[id]->is_modifier() ?
|
||||
ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART,
|
||||
ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
|
||||
model_object->volumes[id]->config.has("extruder") ?
|
||||
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
|
||||
false);
|
||||
|
|
@ -1962,15 +1972,15 @@ void ObjectList::change_part_type()
|
|||
if (!volume)
|
||||
return;
|
||||
|
||||
const auto type = volume->type();
|
||||
if (type == ModelVolume::MODEL_PART)
|
||||
const ModelVolumeType type = volume->type();
|
||||
if (type == ModelVolumeType::MODEL_PART)
|
||||
{
|
||||
const int obj_idx = get_selected_obj_idx();
|
||||
if (obj_idx < 0) return;
|
||||
|
||||
int model_part_cnt = 0;
|
||||
for (auto vol : (*m_objects)[obj_idx]->volumes) {
|
||||
if (vol->type() == ModelVolume::MODEL_PART)
|
||||
if (vol->type() == ModelVolumeType::MODEL_PART)
|
||||
++model_part_cnt;
|
||||
}
|
||||
|
||||
|
|
@ -1982,13 +1992,13 @@ void ObjectList::change_part_type()
|
|||
|
||||
const wxString names[] = { "Part", "Modifier", "Support Enforcer", "Support Blocker" };
|
||||
|
||||
auto new_type = wxGetSingleChoiceIndex("Type: ", _(L("Select type of part")), wxArrayString(4, names), type);
|
||||
auto new_type = ModelVolumeType(wxGetSingleChoiceIndex("Type: ", _(L("Select type of part")), wxArrayString(4, names), int(type)));
|
||||
|
||||
if (new_type == type || new_type < 0)
|
||||
if (new_type == type || new_type == ModelVolumeType::INVALID)
|
||||
return;
|
||||
|
||||
const auto item = GetSelection();
|
||||
volume->set_type(static_cast<ModelVolume::Type>(new_type));
|
||||
volume->set_type(new_type);
|
||||
m_objects_model->SetVolumeType(item, new_type);
|
||||
|
||||
m_parts_changed = true;
|
||||
|
|
@ -1998,11 +2008,11 @@ void ObjectList::change_part_type()
|
|||
//(we show additional settings for Part and Modifier and hide it for Support Blocker/Enforcer)
|
||||
const auto settings_item = m_objects_model->GetSettingsItem(item);
|
||||
if (settings_item &&
|
||||
(new_type == ModelVolume::SUPPORT_ENFORCER || new_type == ModelVolume::SUPPORT_BLOCKER)) {
|
||||
(new_type == ModelVolumeType::SUPPORT_ENFORCER || new_type == ModelVolumeType::SUPPORT_BLOCKER)) {
|
||||
m_objects_model->Delete(settings_item);
|
||||
}
|
||||
else if (!settings_item &&
|
||||
(new_type == ModelVolume::MODEL_PART || new_type == ModelVolume::PARAMETER_MODIFIER)) {
|
||||
(new_type == ModelVolumeType::MODEL_PART || new_type == ModelVolumeType::PARAMETER_MODIFIER)) {
|
||||
select_item(m_objects_model->AddSettingsChild(item));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class ConfigOptionsGroup;
|
|||
class DynamicPrintConfig;
|
||||
class ModelObject;
|
||||
class ModelVolume;
|
||||
enum class ModelVolumeType : int;
|
||||
|
||||
// FIXME: broken build on mac os because of this is missing:
|
||||
typedef std::vector<std::string> t_config_option_keys;
|
||||
|
|
@ -173,7 +174,7 @@ public:
|
|||
void get_freq_settings_choice(const wxString& bundle_name);
|
||||
void update_settings_item();
|
||||
|
||||
void append_menu_item_add_generic(wxMenuItem* menu, const int type);
|
||||
void append_menu_item_add_generic(wxMenuItem* menu, const ModelVolumeType type);
|
||||
void append_menu_items_add_volume(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_split(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_settings(wxMenu* menu);
|
||||
|
|
@ -190,9 +191,9 @@ public:
|
|||
|
||||
void update_opt_keys(t_config_option_keys& t_optopt_keys);
|
||||
|
||||
void load_subobject(int type);
|
||||
void load_part(ModelObject* model_object, wxArrayString& part_names, int type);
|
||||
void load_generic_subobject(const std::string& type_name, const int type);
|
||||
void load_subobject(ModelVolumeType type);
|
||||
void load_part(ModelObject* model_object, wxArrayString& part_names, ModelVolumeType type);
|
||||
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
|
||||
void del_object(const int obj_idx);
|
||||
void del_subobject_item(wxDataViewItem& item);
|
||||
void del_settings_from_config();
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
#endif // __APPLE__
|
||||
{
|
||||
m_og->set_name(_(L("Object Manipulation")));
|
||||
m_og->label_width = 125;
|
||||
m_og->label_width = 12 * wxGetApp().em_unit();//125;
|
||||
m_og->set_grid_vgap(5);
|
||||
|
||||
m_og->m_on_change = std::bind(&ObjectManipulation::on_change, this, std::placeholders::_1, std::placeholders::_2);
|
||||
|
|
@ -44,15 +44,17 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
def.label = L("Name");
|
||||
def.gui_type = "legend";
|
||||
def.tooltip = L("Object name");
|
||||
def.width = 200;
|
||||
def.width = 21 * wxGetApp().em_unit();
|
||||
def.default_value = new ConfigOptionString{ " " };
|
||||
m_og->append_single_option_line(Option(def, "object_name"));
|
||||
|
||||
const int field_width = 5 * wxGetApp().em_unit()/*50*/;
|
||||
|
||||
// Legend for object modification
|
||||
auto line = Line{ "", "" };
|
||||
def.label = "";
|
||||
def.type = coString;
|
||||
def.width = 50;
|
||||
def.width = field_width/*50*/;
|
||||
|
||||
std::vector<std::string> axes{ "x", "y", "z" };
|
||||
for (const auto axis : axes) {
|
||||
|
|
@ -64,13 +66,13 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
m_og->append_line(line);
|
||||
|
||||
|
||||
auto add_og_to_object_settings = [this](const std::string& option_name, const std::string& sidetext)
|
||||
auto add_og_to_object_settings = [this, field_width](const std::string& option_name, const std::string& sidetext)
|
||||
{
|
||||
Line line = { _(option_name), "" };
|
||||
ConfigOptionDef def;
|
||||
def.type = coFloat;
|
||||
def.default_value = new ConfigOptionFloat(0.0);
|
||||
def.width = 50;
|
||||
def.width = field_width/*50*/;
|
||||
|
||||
// Add "uniform scaling" button in front of "Scale" option
|
||||
if (option_name == "Scale") {
|
||||
|
|
@ -89,7 +91,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
|
|||
else if (option_name == "Size") {
|
||||
line.near_label_widget = [this](wxWindow* parent) {
|
||||
return new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap, wxDefaultPosition,
|
||||
wxBitmap(from_u8(var("one_layer_lock_on.png")), wxBITMAP_TYPE_PNG).GetSize());
|
||||
// wxBitmap(from_u8(var("one_layer_lock_on.png")), wxBITMAP_TYPE_PNG).GetSize());
|
||||
create_scaled_bitmap("one_layer_lock_on.png").GetSize());
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,8 @@ void ObjectSettings::update_settings_list()
|
|||
{
|
||||
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(from_u8(var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG),
|
||||
// auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(from_u8(var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG),
|
||||
auto btn = new wxBitmapButton(parent, wxID_ANY, create_scaled_bitmap("colorchange_delete_on.png"),
|
||||
wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||
#ifdef __WXMSW__
|
||||
btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
|
|
@ -98,7 +99,7 @@ void ObjectSettings::update_settings_list()
|
|||
std::vector<std::string> categories;
|
||||
if (!(opt_keys.size() == 1 && opt_keys[0] == "extruder"))// return;
|
||||
{
|
||||
auto extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
|
||||
const int extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
|
||||
wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||
|
||||
for (auto& opt_key : opt_keys) {
|
||||
|
|
@ -119,8 +120,8 @@ void ObjectSettings::update_settings_list()
|
|||
continue;
|
||||
|
||||
auto optgroup = std::make_shared<ConfigOptionsGroup>(m_parent, cat.first, config, false, extra_column);
|
||||
optgroup->label_width = 150;
|
||||
optgroup->sidetext_width = 70;
|
||||
optgroup->label_width = 15 * wxGetApp().em_unit();//150;
|
||||
optgroup->sidetext_width = 7 * wxGetApp().em_unit();//70;
|
||||
|
||||
optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) {
|
||||
wxGetApp().obj_list()->part_settings_changed(); };
|
||||
|
|
@ -130,7 +131,7 @@ void ObjectSettings::update_settings_list()
|
|||
if (opt == "extruder")
|
||||
continue;
|
||||
Option option = optgroup->get_option(opt);
|
||||
option.opt.width = 70;
|
||||
option.opt.width = 7 * wxGetApp().em_unit();//70;
|
||||
optgroup->append_single_option_line(option);
|
||||
}
|
||||
optgroup->reload_config();
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ View3D::~View3D()
|
|||
|
||||
bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process)
|
||||
{
|
||||
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize))
|
||||
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
|
||||
return false;
|
||||
|
||||
m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this);
|
||||
|
|
@ -69,9 +69,6 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba
|
|||
m_canvas->set_config(config);
|
||||
m_canvas->enable_gizmos(true);
|
||||
m_canvas->enable_toolbar(true);
|
||||
#if !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
m_canvas->enable_force_zoom_to_bed(true);
|
||||
#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
|
||||
#if !ENABLE_IMGUI
|
||||
m_gizmo_widget = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
|
||||
|
|
@ -92,6 +89,12 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba
|
|||
return true;
|
||||
}
|
||||
|
||||
void View3D::set_bed(Bed3D* bed)
|
||||
{
|
||||
if (m_canvas != nullptr)
|
||||
m_canvas->set_bed(bed);
|
||||
}
|
||||
|
||||
void View3D::set_view_toolbar(GLToolbar* toolbar)
|
||||
{
|
||||
if (m_canvas != nullptr)
|
||||
|
|
@ -104,15 +107,10 @@ void View3D::set_as_dirty()
|
|||
m_canvas->set_as_dirty();
|
||||
}
|
||||
|
||||
void View3D::set_bed_shape(const Pointfs& shape)
|
||||
void View3D::bed_shape_changed()
|
||||
{
|
||||
if (m_canvas != nullptr)
|
||||
{
|
||||
m_canvas->set_bed_shape(shape);
|
||||
#if !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
m_canvas->zoom_to_bed();
|
||||
#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
}
|
||||
m_canvas->bed_shape_changed();
|
||||
}
|
||||
|
||||
void View3D::select_view(const std::string& direction)
|
||||
|
|
@ -229,7 +227,7 @@ bool Preview::init(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlici
|
|||
if ((config == nullptr) || (process == nullptr) || (gcode_preview_data == nullptr))
|
||||
return false;
|
||||
|
||||
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize))
|
||||
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
|
||||
return false;
|
||||
|
||||
m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this);
|
||||
|
|
@ -259,7 +257,7 @@ bool Preview::init(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlici
|
|||
m_label_show_features = new wxStaticText(this, wxID_ANY, _(L("Show")));
|
||||
|
||||
m_combochecklist_features = new wxComboCtrl();
|
||||
m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxSize(200, -1), wxCB_READONLY);
|
||||
m_combochecklist_features->Create(this, wxID_ANY, _(L("Feature types")), wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), wxCB_READONLY);
|
||||
std::string feature_text = GUI::into_u8(_(L("Feature types")));
|
||||
std::string feature_items = GUI::into_u8(
|
||||
_(L("Perimeter")) + "|" +
|
||||
|
|
@ -345,6 +343,12 @@ Preview::~Preview()
|
|||
}
|
||||
}
|
||||
|
||||
void Preview::set_bed(Bed3D* bed)
|
||||
{
|
||||
if (m_canvas != nullptr)
|
||||
m_canvas->set_bed(bed);
|
||||
}
|
||||
|
||||
void Preview::set_view_toolbar(GLToolbar* toolbar)
|
||||
{
|
||||
if (m_canvas != nullptr)
|
||||
|
|
@ -376,9 +380,10 @@ void Preview::set_enabled(bool enabled)
|
|||
m_enabled = enabled;
|
||||
}
|
||||
|
||||
void Preview::set_bed_shape(const Pointfs& shape)
|
||||
void Preview::bed_shape_changed()
|
||||
{
|
||||
m_canvas->set_bed_shape(shape);
|
||||
if (m_canvas != nullptr)
|
||||
m_canvas->bed_shape_changed();
|
||||
}
|
||||
|
||||
void Preview::select_view(const std::string& direction)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ namespace GUI {
|
|||
|
||||
class GLCanvas3D;
|
||||
class GLToolbar;
|
||||
class Bed3D;
|
||||
|
||||
class View3D : public wxPanel
|
||||
{
|
||||
|
|
@ -48,10 +49,11 @@ public:
|
|||
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
||||
GLCanvas3D* get_canvas3d() { return m_canvas; }
|
||||
|
||||
void set_bed(Bed3D* bed);
|
||||
void set_view_toolbar(GLToolbar* toolbar);
|
||||
|
||||
void set_as_dirty();
|
||||
void set_bed_shape(const Pointfs& shape);
|
||||
void bed_shape_changed();
|
||||
|
||||
void select_view(const std::string& direction);
|
||||
void select_all();
|
||||
|
|
@ -114,12 +116,13 @@ public:
|
|||
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
|
||||
GLCanvas3D* get_canvas3d() { return m_canvas; }
|
||||
|
||||
void set_bed(Bed3D* bed);
|
||||
void set_view_toolbar(GLToolbar* toolbar);
|
||||
|
||||
void set_number_extruders(unsigned int number_extruders);
|
||||
void set_canvas_as_dirty();
|
||||
void set_enabled(bool enabled);
|
||||
void set_bed_shape(const Pointfs& shape);
|
||||
void bed_shape_changed();
|
||||
void select_view(const std::string& direction);
|
||||
void set_viewport_from_scene(GLCanvas3D* canvas);
|
||||
void set_viewport_into_scene(GLCanvas3D* canvas);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <wx/string.h>
|
||||
#include <wx/event.h>
|
||||
#include <wx/clipbrd.h>
|
||||
#include <wx/debug.h>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
|
@ -25,10 +26,12 @@ namespace GUI {
|
|||
|
||||
|
||||
ImGuiWrapper::ImGuiWrapper()
|
||||
: m_font_texture(0)
|
||||
: m_glyph_ranges(nullptr)
|
||||
, m_font_texture(0)
|
||||
, m_style_scaling(1.0)
|
||||
, m_mouse_buttons(0)
|
||||
, m_disabled(false)
|
||||
, m_new_frame_open(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -43,12 +46,43 @@ bool ImGuiWrapper::init()
|
|||
ImGui::CreateContext();
|
||||
|
||||
init_default_font(m_style_scaling);
|
||||
init_input();
|
||||
init_style();
|
||||
|
||||
ImGui::GetIO().IniFilename = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::set_language(const std::string &language)
|
||||
{
|
||||
const ImWchar *ranges = nullptr;
|
||||
size_t idx = language.find('_');
|
||||
std::string lang = (idx == std::string::npos) ? language : language.substr(0, idx);
|
||||
static const ImWchar ranges_latin2[] =
|
||||
{
|
||||
0x0020, 0x00FF, // Basic Latin + Latin Supplement
|
||||
0x0100, 0x017F, // Latin Extended-A
|
||||
0,
|
||||
};
|
||||
if (lang == "cs" || lang == "pl") {
|
||||
ranges = ranges_latin2;
|
||||
} else if (lang == "ru" || lang == "uk") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesCyrillic();
|
||||
} else if (lang == "jp") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesJapanese();
|
||||
} else if (lang == "kr") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesKorean();
|
||||
} else if (lang == "zh") {
|
||||
ranges = ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon();
|
||||
}
|
||||
|
||||
if (ranges != m_glyph_ranges) {
|
||||
m_glyph_ranges = ranges;
|
||||
init_default_font(m_style_scaling);
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiWrapper::set_display_size(float w, float h)
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
|
@ -67,6 +101,10 @@ void ImGuiWrapper::set_style_scaling(float scaling)
|
|||
|
||||
bool ImGuiWrapper::update_mouse_data(wxMouseEvent& evt)
|
||||
{
|
||||
if (! display_initialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.MousePos = ImVec2((float)evt.GetX(), (float)evt.GetY());
|
||||
io.MouseDown[0] = evt.LeftDown();
|
||||
|
|
@ -74,23 +112,63 @@ bool ImGuiWrapper::update_mouse_data(wxMouseEvent& evt)
|
|||
io.MouseDown[2] = evt.MiddleDown();
|
||||
|
||||
unsigned buttons = (evt.LeftDown() ? 1 : 0) | (evt.RightDown() ? 2 : 0) | (evt.MiddleDown() ? 4 : 0);
|
||||
bool res = buttons != m_mouse_buttons;
|
||||
m_mouse_buttons = buttons;
|
||||
return res;
|
||||
|
||||
new_frame();
|
||||
return want_mouse();
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::update_key_data(wxKeyEvent &evt)
|
||||
{
|
||||
if (! display_initialized()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
if (evt.GetEventType() == wxEVT_CHAR) {
|
||||
// Char event
|
||||
const auto key = evt.GetUnicodeKey();
|
||||
if (key != 0) {
|
||||
io.AddInputCharacter(key);
|
||||
}
|
||||
|
||||
new_frame();
|
||||
return want_keyboard() || want_text_input();
|
||||
} else {
|
||||
// Key up/down event
|
||||
int key = evt.GetKeyCode();
|
||||
wxCHECK_MSG(key >= 0 && key < IM_ARRAYSIZE(io.KeysDown), false, "Received invalid key code");
|
||||
|
||||
io.KeysDown[key] = evt.GetEventType() == wxEVT_KEY_DOWN;
|
||||
io.KeyShift = evt.ShiftDown();
|
||||
io.KeyCtrl = evt.ControlDown();
|
||||
io.KeyAlt = evt.AltDown();
|
||||
io.KeySuper = evt.MetaDown();
|
||||
|
||||
new_frame();
|
||||
return want_keyboard() || want_text_input();
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiWrapper::new_frame()
|
||||
{
|
||||
if (m_new_frame_open) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_font_texture == 0)
|
||||
create_device_objects();
|
||||
|
||||
ImGui::NewFrame();
|
||||
m_new_frame_open = true;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::render()
|
||||
{
|
||||
ImGui::Render();
|
||||
render_draw_data(ImGui::GetDrawData());
|
||||
m_new_frame_open = false;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::set_next_window_pos(float x, float y, int flag)
|
||||
|
|
@ -125,6 +203,12 @@ bool ImGuiWrapper::button(const wxString &label)
|
|||
return ImGui::Button(label_utf8.c_str());
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::radio_button(const wxString &label, bool active)
|
||||
{
|
||||
auto label_utf8 = into_u8(label);
|
||||
return ImGui::RadioButton(label_utf8.c_str(), active);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
|
||||
{
|
||||
return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str());
|
||||
|
|
@ -161,6 +245,28 @@ void ImGuiWrapper::text(const wxString &label)
|
|||
ImGui::Text(label_utf8.c_str(), NULL);
|
||||
}
|
||||
|
||||
|
||||
bool ImGuiWrapper::combo(const wxString& label, const std::vector<wxString>& options, wxString& selection)
|
||||
{
|
||||
std::string selection_u8 = into_u8(selection);
|
||||
|
||||
// this is to force the label to the left of the widget:
|
||||
text(label);
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::BeginCombo("", selection_u8.c_str())) {
|
||||
for (const wxString& option : options) {
|
||||
std::string option_u8 = into_u8(option);
|
||||
bool is_selected = (selection_u8.empty()) ? false : (option_u8 == selection_u8);
|
||||
if (ImGui::Selectable(option_u8.c_str(), is_selected))
|
||||
selection = option_u8;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::disabled_begin(bool disabled)
|
||||
{
|
||||
wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call");
|
||||
|
|
@ -210,7 +316,7 @@ void ImGuiWrapper::init_default_font(float scaling)
|
|||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->Clear();
|
||||
ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), font_size * scaling);
|
||||
ImFont* font = io.Fonts->AddFontFromFileTTF((Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf").c_str(), font_size * scaling, nullptr, m_glyph_ranges);
|
||||
if (font == nullptr) {
|
||||
font = io.Fonts->AddFontDefault();
|
||||
if (font == nullptr) {
|
||||
|
|
@ -249,6 +355,82 @@ void ImGuiWrapper::create_fonts_texture()
|
|||
glBindTexture(GL_TEXTURE_2D, last_texture);
|
||||
}
|
||||
|
||||
void ImGuiWrapper::init_input()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
|
||||
io.KeyMap[ImGuiKey_Tab] = WXK_TAB;
|
||||
io.KeyMap[ImGuiKey_LeftArrow] = WXK_LEFT;
|
||||
io.KeyMap[ImGuiKey_RightArrow] = WXK_RIGHT;
|
||||
io.KeyMap[ImGuiKey_UpArrow] = WXK_UP;
|
||||
io.KeyMap[ImGuiKey_DownArrow] = WXK_DOWN;
|
||||
io.KeyMap[ImGuiKey_PageUp] = WXK_PAGEUP;
|
||||
io.KeyMap[ImGuiKey_PageDown] = WXK_PAGEDOWN;
|
||||
io.KeyMap[ImGuiKey_Home] = WXK_HOME;
|
||||
io.KeyMap[ImGuiKey_End] = WXK_END;
|
||||
io.KeyMap[ImGuiKey_Insert] = WXK_INSERT;
|
||||
io.KeyMap[ImGuiKey_Delete] = WXK_DELETE;
|
||||
io.KeyMap[ImGuiKey_Backspace] = WXK_BACK;
|
||||
io.KeyMap[ImGuiKey_Space] = WXK_SPACE;
|
||||
io.KeyMap[ImGuiKey_Enter] = WXK_RETURN;
|
||||
io.KeyMap[ImGuiKey_Escape] = WXK_ESCAPE;
|
||||
io.KeyMap[ImGuiKey_A] = 'A';
|
||||
io.KeyMap[ImGuiKey_C] = 'C';
|
||||
io.KeyMap[ImGuiKey_V] = 'V';
|
||||
io.KeyMap[ImGuiKey_X] = 'X';
|
||||
io.KeyMap[ImGuiKey_Y] = 'Y';
|
||||
io.KeyMap[ImGuiKey_Z] = 'Z';
|
||||
|
||||
// Don't let imgui special-case Mac, wxWidgets already do that
|
||||
io.ConfigMacOSXBehaviors = false;
|
||||
|
||||
// Setup clipboard interaction callbacks
|
||||
io.SetClipboardTextFn = clipboard_set;
|
||||
io.GetClipboardTextFn = clipboard_get;
|
||||
io.ClipboardUserData = this;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::init_style()
|
||||
{
|
||||
ImGuiStyle &style = ImGui::GetStyle();
|
||||
|
||||
auto set_color = [&](ImGuiCol_ col, unsigned hex_color) {
|
||||
style.Colors[col] = ImVec4(
|
||||
((hex_color >> 24) & 0xff) / 255.0f,
|
||||
((hex_color >> 16) & 0xff) / 255.0f,
|
||||
((hex_color >> 8) & 0xff) / 255.0f,
|
||||
(hex_color & 0xff) / 255.0f);
|
||||
};
|
||||
|
||||
static const unsigned COL_GREY_DARK = 0x444444ff;
|
||||
static const unsigned COL_GREY_LIGHT = 0x666666ff;
|
||||
static const unsigned COL_ORANGE_DARK = 0xba5418ff;
|
||||
static const unsigned COL_ORANGE_LIGHT = 0xff6f22ff;
|
||||
|
||||
// Generics
|
||||
set_color(ImGuiCol_TitleBgActive, COL_ORANGE_DARK);
|
||||
set_color(ImGuiCol_FrameBg, COL_GREY_DARK);
|
||||
set_color(ImGuiCol_FrameBgHovered, COL_GREY_LIGHT);
|
||||
set_color(ImGuiCol_FrameBgActive, COL_GREY_LIGHT);
|
||||
|
||||
// Text selection
|
||||
set_color(ImGuiCol_TextSelectedBg, COL_ORANGE_DARK);
|
||||
|
||||
// Buttons
|
||||
set_color(ImGuiCol_Button, COL_ORANGE_DARK);
|
||||
set_color(ImGuiCol_ButtonHovered, COL_ORANGE_LIGHT);
|
||||
set_color(ImGuiCol_ButtonActive, COL_ORANGE_LIGHT);
|
||||
|
||||
// Checkbox
|
||||
set_color(ImGuiCol_CheckMark, COL_ORANGE_LIGHT);
|
||||
|
||||
// ComboBox items
|
||||
set_color(ImGuiCol_Header, COL_ORANGE_DARK);
|
||||
set_color(ImGuiCol_HeaderHovered, COL_ORANGE_LIGHT);
|
||||
set_color(ImGuiCol_HeaderActive, COL_ORANGE_LIGHT);
|
||||
}
|
||||
|
||||
void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)
|
||||
{
|
||||
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||
|
|
@ -347,6 +529,12 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)
|
|||
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::display_initialized() const
|
||||
{
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
return io.DisplaySize.x >= 0.0f && io.DisplaySize.y >= 0.0f;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::destroy_device_objects()
|
||||
{
|
||||
destroy_fonts_texture();
|
||||
|
|
@ -362,6 +550,37 @@ void ImGuiWrapper::destroy_fonts_texture()
|
|||
}
|
||||
}
|
||||
|
||||
const char* ImGuiWrapper::clipboard_get(void* user_data)
|
||||
{
|
||||
ImGuiWrapper *self = reinterpret_cast<ImGuiWrapper*>(user_data);
|
||||
|
||||
const char* res = "";
|
||||
|
||||
if (wxTheClipboard->Open()) {
|
||||
if (wxTheClipboard->IsSupported(wxDF_TEXT)) {
|
||||
wxTextDataObject data;
|
||||
wxTheClipboard->GetData(data);
|
||||
|
||||
if (data.GetTextLength() > 0) {
|
||||
self->m_clipboard_text = into_u8(data.GetText());
|
||||
res = self->m_clipboard_text.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
wxTheClipboard->Close();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::clipboard_set(void* /* user_data */, const char* text)
|
||||
{
|
||||
if (wxTheClipboard->Open()) {
|
||||
wxTheClipboard->SetData(new wxTextDataObject(wxString::FromUTF8(text))); // object owned by the clipboard
|
||||
wxTheClipboard->Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
class wxString;
|
||||
class wxMouseEvent;
|
||||
class wxKeyEvent;
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -20,10 +21,13 @@ class ImGuiWrapper
|
|||
typedef std::map<std::string, ImFont*> FontsMap;
|
||||
|
||||
FontsMap m_fonts;
|
||||
const ImWchar *m_glyph_ranges;
|
||||
unsigned m_font_texture;
|
||||
float m_style_scaling;
|
||||
unsigned m_mouse_buttons;
|
||||
bool m_disabled;
|
||||
bool m_new_frame_open;
|
||||
std::string m_clipboard_text;
|
||||
|
||||
public:
|
||||
ImGuiWrapper();
|
||||
|
|
@ -32,9 +36,11 @@ public:
|
|||
bool init();
|
||||
void read_glsl_version();
|
||||
|
||||
void set_language(const std::string &language);
|
||||
void set_display_size(float w, float h);
|
||||
void set_style_scaling(float scaling);
|
||||
bool update_mouse_data(wxMouseEvent &evt);
|
||||
bool update_key_data(wxKeyEvent &evt);
|
||||
|
||||
void new_frame();
|
||||
void render();
|
||||
|
|
@ -47,10 +53,12 @@ public:
|
|||
void end();
|
||||
|
||||
bool button(const wxString &label);
|
||||
bool radio_button(const wxString &label, bool active);
|
||||
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
|
||||
bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
|
||||
bool checkbox(const wxString &label, bool &value);
|
||||
void text(const wxString &label);
|
||||
bool combo(const wxString& label, const std::vector<wxString>& options, wxString& current_selection);
|
||||
|
||||
void disabled_begin(bool disabled);
|
||||
void disabled_end();
|
||||
|
|
@ -59,13 +67,20 @@ public:
|
|||
bool want_keyboard() const;
|
||||
bool want_text_input() const;
|
||||
bool want_any_input() const;
|
||||
|
||||
private:
|
||||
void init_default_font(float scaling);
|
||||
void create_device_objects();
|
||||
void create_fonts_texture();
|
||||
void init_input();
|
||||
void init_style();
|
||||
void render_draw_data(ImDrawData *draw_data);
|
||||
bool display_initialized() const;
|
||||
void destroy_device_objects();
|
||||
void destroy_fonts_texture();
|
||||
|
||||
static const char* clipboard_get(void* user_data);
|
||||
static void clipboard_set(void* user_data, const char* text);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "GUI.hpp"
|
||||
#include <wx/scrolwin.h>
|
||||
#include "GUI_App.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
|
@ -16,7 +17,8 @@ KBShortcutsDialog::KBShortcutsDialog()
|
|||
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// logo
|
||||
wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_32px.png")), wxBITMAP_TYPE_PNG);
|
||||
// wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_32px.png")), wxBITMAP_TYPE_PNG);
|
||||
const wxBitmap logo_bmp = create_scaled_bitmap("Slic3r_32px.png");
|
||||
|
||||
// fonts
|
||||
wxFont head_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold();
|
||||
|
|
@ -54,7 +56,7 @@ KBShortcutsDialog::KBShortcutsDialog()
|
|||
hsizer->Add(logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 15);
|
||||
|
||||
// head
|
||||
wxStaticText* head = new wxStaticText(panel, wxID_ANY, sc.first, wxDefaultPosition, wxSize(200,-1));
|
||||
wxStaticText* head = new wxStaticText(panel, wxID_ANY, sc.first, wxDefaultPosition, wxSize(20 * wxGetApp().em_unit(), -1));
|
||||
head->SetFont(head_font);
|
||||
hsizer->Add(head, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,11 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
|||
SLIC3R_VERSION +
|
||||
_(L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases")));
|
||||
|
||||
|
||||
// initialize default width_unit according to the width of the one symbol ("x") of the current system font
|
||||
const wxSize size = GetTextExtent("m");
|
||||
wxGetApp().set_em_unit(size.x-1);
|
||||
|
||||
// initialize tabpanel and menubar
|
||||
init_tabpanel();
|
||||
init_menubar();
|
||||
|
|
@ -105,6 +110,12 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
|||
event.Skip();
|
||||
});
|
||||
|
||||
Bind(wxEVT_ACTIVATE, [this](wxActivateEvent& event) {
|
||||
if (m_plater != nullptr && event.GetActive())
|
||||
m_plater->on_activate();
|
||||
event.Skip();
|
||||
});
|
||||
|
||||
wxGetApp().persist_window_geometry(this);
|
||||
|
||||
update_ui_from_settings(); // FIXME (?)
|
||||
|
|
@ -136,11 +147,11 @@ void MainFrame::init_tabpanel()
|
|||
wxGetApp().obj_list()->create_popup_menus();
|
||||
|
||||
// The following event is emited by Tab implementation on config value change.
|
||||
Bind(EVT_TAB_VALUE_CHANGED, &MainFrame::on_value_changed, this);
|
||||
Bind(EVT_TAB_VALUE_CHANGED, &MainFrame::on_value_changed, this); // #ys_FIXME_to_delete
|
||||
|
||||
// The following event is emited by Tab on preset selection,
|
||||
// or when the preset's "modified" status changes.
|
||||
Bind(EVT_TAB_PRESETS_CHANGED, &MainFrame::on_presets_changed, this);
|
||||
Bind(EVT_TAB_PRESETS_CHANGED, &MainFrame::on_presets_changed, this); // #ys_FIXME_to_delete
|
||||
|
||||
create_preset_tabs();
|
||||
|
||||
|
|
@ -822,6 +833,7 @@ void MainFrame::select_view(const std::string& direction)
|
|||
m_plater->select_view(direction);
|
||||
}
|
||||
|
||||
// #ys_FIXME_to_delete
|
||||
void MainFrame::on_presets_changed(SimpleEvent &event)
|
||||
{
|
||||
auto *tab = dynamic_cast<Tab*>(event.GetEventObject());
|
||||
|
|
@ -846,6 +858,7 @@ void MainFrame::on_presets_changed(SimpleEvent &event)
|
|||
}
|
||||
}
|
||||
|
||||
// #ys_FIXME_to_delete
|
||||
void MainFrame::on_value_changed(wxCommandEvent& event)
|
||||
{
|
||||
auto *tab = dynamic_cast<Tab*>(event.GetEventObject());
|
||||
|
|
@ -861,12 +874,12 @@ void MainFrame::on_value_changed(wxCommandEvent& event)
|
|||
m_plater->on_extruders_change(value);
|
||||
}
|
||||
}
|
||||
// Don't save while loading for the first time.
|
||||
if (m_loaded) {
|
||||
AppConfig &cfg = *wxGetApp().app_config;
|
||||
if (cfg.get("autosave") == "1")
|
||||
cfg.save();
|
||||
}
|
||||
}
|
||||
|
||||
void MainFrame::on_config_changed(DynamicPrintConfig* config) const
|
||||
{
|
||||
if (m_plater)
|
||||
m_plater->on_config_change(*config); // propagate config change events to the plater
|
||||
}
|
||||
|
||||
// Called after the Preferences dialog is closed and the program settings are saved.
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ public:
|
|||
void load_config(const DynamicPrintConfig& config);
|
||||
void select_tab(size_t tab) const;
|
||||
void select_view(const std::string& direction);
|
||||
// Propagate changed configuration from the Tab to the Platter and save changes to the AppConfig
|
||||
void on_config_changed(DynamicPrintConfig* cfg) const ;
|
||||
|
||||
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
|
||||
|
||||
|
|
|
|||
|
|
@ -7,19 +7,25 @@
|
|||
#include <wx/statbmp.h>
|
||||
#include <wx/scrolwin.h>
|
||||
#include <wx/clipbrd.h>
|
||||
#include <wx/html/htmlwin.h>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "ConfigWizard.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id) :
|
||||
MsgDialog(parent, title, headline, wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG), button_id)
|
||||
// MsgDialog(parent, title, headline, wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG), button_id)
|
||||
MsgDialog(parent, title, headline, create_scaled_bitmap("Slic3r_192px.png"), button_id)
|
||||
{}
|
||||
|
||||
MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id) :
|
||||
|
|
@ -35,7 +41,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
|
|||
|
||||
auto *headtext = new wxStaticText(this, wxID_ANY, headline);
|
||||
headtext->SetFont(boldfont);
|
||||
headtext->Wrap(CONTENT_WIDTH);
|
||||
headtext->Wrap(CONTENT_WIDTH*wxGetApp().em_unit());
|
||||
rightsizer->Add(headtext);
|
||||
rightsizer->AddSpacer(VERT_SPACING);
|
||||
|
||||
|
|
@ -47,7 +53,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
|
|||
btn_sizer->Add(button);
|
||||
}
|
||||
|
||||
rightsizer->Add(btn_sizer, 0, wxALIGN_CENTRE_HORIZONTAL);
|
||||
rightsizer->Add(btn_sizer, 0, wxALIGN_RIGHT);
|
||||
|
||||
auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(bitmap));
|
||||
|
||||
|
|
@ -64,38 +70,36 @@ MsgDialog::~MsgDialog() {}
|
|||
|
||||
ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg)
|
||||
: MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")),
|
||||
wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG),
|
||||
// wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG),
|
||||
create_scaled_bitmap("Slic3r_192px_grayscale.png"),
|
||||
wxID_NONE)
|
||||
, msg(msg)
|
||||
{
|
||||
auto *panel = new wxScrolledWindow(this);
|
||||
auto *p_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
panel->SetSizer(p_sizer);
|
||||
|
||||
auto *text = new wxStaticText(panel, wxID_ANY, msg);
|
||||
text->Wrap(CONTENT_WIDTH);
|
||||
p_sizer->Add(text, 1, wxEXPAND);
|
||||
|
||||
panel->SetMinSize(wxSize(CONTENT_WIDTH, 0));
|
||||
panel->SetScrollRate(0, 5);
|
||||
|
||||
content_sizer->Add(panel, 1, wxEXPAND);
|
||||
|
||||
auto *btn_copy = new wxButton(this, wxID_ANY, _(L("Copy to clipboard")));
|
||||
btn_copy->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) {
|
||||
if (wxTheClipboard->Open()) {
|
||||
wxTheClipboard->SetData(new wxTextDataObject(this->msg)); // Note: the clipboard takes ownership of the pointer
|
||||
wxTheClipboard->Close();
|
||||
// Text shown as HTML, so that mouse selection and Ctrl-V to copy will work.
|
||||
wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO);
|
||||
{
|
||||
html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), -1));
|
||||
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK); // wxSYS_COLOUR_WINDOW
|
||||
auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
|
||||
auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
|
||||
const int font_size = font.GetPointSize()-1;
|
||||
int size[] = {font_size, font_size, font_size, font_size, font_size, font_size, font_size};
|
||||
html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
|
||||
html->SetBorders(2);
|
||||
std::string msg_escaped = xml_escape(msg.ToUTF8().data());
|
||||
boost::replace_all(msg_escaped, "\r\n", "<br>");
|
||||
boost::replace_all(msg_escaped, "\n", "<br>");
|
||||
html->SetPage("<html><body bgcolor=\"" + bgr_clr_str + "\"><font color=\"" + text_clr_str + "\">" + wxString::FromUTF8(msg_escaped.data()) + "</font></body></html>");
|
||||
content_sizer->Add(html, 1, wxEXPAND);
|
||||
}
|
||||
});
|
||||
|
||||
auto *btn_ok = new wxButton(this, wxID_OK);
|
||||
btn_ok->SetFocus();
|
||||
btn_sizer->Add(btn_ok, 0, wxRIGHT, HORIZ_SPACING);
|
||||
|
||||
btn_sizer->Add(btn_copy, 0, wxRIGHT, HORIZ_SPACING);
|
||||
btn_sizer->Add(btn_ok);
|
||||
|
||||
SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT));
|
||||
SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT*wxGetApp().em_unit()));
|
||||
Fit();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ struct MsgDialog : wxDialog
|
|||
|
||||
protected:
|
||||
enum {
|
||||
CONTENT_WIDTH = 500,
|
||||
CONTENT_MAX_HEIGHT = 600,
|
||||
CONTENT_WIDTH = 50,
|
||||
CONTENT_MAX_HEIGHT = 60,
|
||||
BORDER = 30,
|
||||
VERT_SPACING = 15,
|
||||
HORIZ_SPACING = 5,
|
||||
|
|
|
|||
|
|
@ -113,7 +113,6 @@ void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& fiel
|
|||
}
|
||||
|
||||
void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = nullptr*/) {
|
||||
//! if (line.sizer != nullptr || (line.widget != nullptr && line.full_width > 0)) {
|
||||
if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width) {
|
||||
if (line.sizer != nullptr) {
|
||||
sizer->Add(line.sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 15);
|
||||
|
|
@ -135,6 +134,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
|
||||
// if we have a single option with no label, no sidetext just add it directly to sizer
|
||||
if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width &&
|
||||
option_set.front().opt.label.empty() &&
|
||||
option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr &&
|
||||
line.get_extra_widgets().size() == 0) {
|
||||
wxSizer* tmp_sizer;
|
||||
|
|
@ -179,12 +179,12 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
// Text is properly aligned only when Ellipsize is checked.
|
||||
label_style |= staticbox ? 0 : wxST_ELLIPSIZE_END;
|
||||
#endif /* __WXGTK__ */
|
||||
label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ":"),
|
||||
label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ": "),
|
||||
wxDefaultPosition, wxSize(label_width, -1), label_style);
|
||||
label->SetFont(label_font);
|
||||
label->Wrap(label_width); // avoid a Linux/GTK bug
|
||||
if (!line.near_label_widget)
|
||||
grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5);
|
||||
grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, line.label.IsEmpty() ? 0 : 5);
|
||||
else {
|
||||
// If we're here, we have some widget near the label
|
||||
// so we need a horizontal sizer to arrange these things
|
||||
|
|
@ -213,6 +213,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1);
|
||||
// If we have a single option with no sidetext just add it directly to the grid sizer
|
||||
if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 &&
|
||||
option_set.front().opt.label.empty() &&
|
||||
option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) {
|
||||
const auto& option = option_set.front();
|
||||
const auto& field = build_field(option, label);
|
||||
|
|
@ -236,7 +237,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
wxString str_label = (option.label == "Top" || option.label == "Bottom") ?
|
||||
_CTX(option.label, "Layers") :
|
||||
_(option.label);
|
||||
label = new wxStaticText(parent(), wxID_ANY, str_label + ":", wxDefaultPosition, wxDefaultSize);
|
||||
label = new wxStaticText(parent(), wxID_ANY, str_label + ": ", wxDefaultPosition, wxDefaultSize);
|
||||
label->SetFont(label_font);
|
||||
sizer_tmp->Add(label, 0, /*wxALIGN_RIGHT |*/ wxALIGN_CENTER_VERTICAL, 0);
|
||||
}
|
||||
|
|
@ -245,6 +246,16 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
const Option& opt_ref = opt;
|
||||
auto& field = build_field(opt_ref, label);
|
||||
add_undo_buttuns_to_sizer(sizer_tmp, field);
|
||||
if (option_set.size() == 1 && option_set.front().opt.full_width)
|
||||
{
|
||||
const auto v_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer_tmp->Add(v_sizer, 1, wxEXPAND);
|
||||
is_sizer_field(field) ?
|
||||
v_sizer->Add(field->getSizer(), 0, wxEXPAND) :
|
||||
v_sizer->Add(field->getWindow(), 0, wxEXPAND);
|
||||
return;
|
||||
}
|
||||
|
||||
is_sizer_field(field) ?
|
||||
sizer_tmp->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) :
|
||||
sizer_tmp->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
|
|
@ -269,7 +280,17 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
|
|||
}
|
||||
}
|
||||
// add extra sizers if any
|
||||
for (auto extra_widget : line.get_extra_widgets()) {
|
||||
for (auto extra_widget : line.get_extra_widgets())
|
||||
{
|
||||
if (line.get_extra_widgets().size() == 1 && !staticbox)
|
||||
{
|
||||
// extra widget for non-staticbox option group (like for the frequently used parameters on the sidebar) should be wxALIGN_RIGHT
|
||||
const auto v_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(v_sizer, 1, wxEXPAND);
|
||||
v_sizer->Add(extra_widget(parent()), 0, wxALIGN_RIGHT);
|
||||
return;
|
||||
}
|
||||
|
||||
sizer->Add(extra_widget(parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); //! requires verification
|
||||
}
|
||||
}
|
||||
|
|
@ -538,8 +559,9 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
|
|||
ret = config.opt_int(opt_key, idx);
|
||||
break;
|
||||
case coEnum:{
|
||||
if (opt_key.compare("external_fill_pattern") == 0 ||
|
||||
opt_key.compare("fill_pattern") == 0 ) {
|
||||
if (opt_key == "top_fill_pattern" ||
|
||||
opt_key == "bottom_fill_pattern" ||
|
||||
opt_key == "fill_pattern" ) {
|
||||
ret = static_cast<int>(config.option<ConfigOptionEnum<InfillPattern>>(opt_key)->value);
|
||||
}
|
||||
else if (opt_key.compare("gcode_flavor") == 0 ) {
|
||||
|
|
@ -593,7 +615,7 @@ Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int op
|
|||
void ogStaticText::SetText(const wxString& value, bool wrap/* = true*/)
|
||||
{
|
||||
SetLabel(value);
|
||||
if (wrap) Wrap(400);
|
||||
if (wrap) Wrap(40 * wxGetApp().em_unit());
|
||||
GetParent()->Layout();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class OptionsGroup {
|
|||
public:
|
||||
const bool staticbox {true};
|
||||
const wxString title {wxString("")};
|
||||
size_t label_width {200};
|
||||
size_t label_width = 20 * wxGetApp().em_unit();// {200};
|
||||
wxSizer* sizer {nullptr};
|
||||
column_t extra_column {nullptr};
|
||||
t_change m_on_change { nullptr };
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#include "GLCanvas3D.hpp"
|
||||
#include "GLToolbar.hpp"
|
||||
#include "GUI_Preview.hpp"
|
||||
#include "3DBed.hpp"
|
||||
#include "Tab.hpp"
|
||||
#include "PresetBundle.hpp"
|
||||
#include "BackgroundSlicingProcess.hpp"
|
||||
|
|
@ -98,6 +99,11 @@ public:
|
|||
wxStaticText *info_facets;
|
||||
wxStaticText *info_materials;
|
||||
wxStaticText *info_manifold;
|
||||
|
||||
wxStaticText *label_volume;
|
||||
wxStaticText *label_materials;
|
||||
std::vector<wxStaticText *> sla_hidden_items;
|
||||
|
||||
bool showing_manifold_warning_icon;
|
||||
void show_sizer(bool show);
|
||||
};
|
||||
|
|
@ -119,15 +125,16 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
|
|||
(*info_label)->SetFont(wxGetApp().small_font());
|
||||
grid_sizer->Add(text, 0);
|
||||
grid_sizer->Add(*info_label, 0);
|
||||
return text;
|
||||
};
|
||||
|
||||
init_info_label(&info_size, _(L("Size")));
|
||||
init_info_label(&info_volume, _(L("Volume")));
|
||||
label_volume = init_info_label(&info_volume, _(L("Volume")));
|
||||
init_info_label(&info_facets, _(L("Facets")));
|
||||
init_info_label(&info_materials, _(L("Materials")));
|
||||
label_materials = init_info_label(&info_materials, _(L("Materials")));
|
||||
Add(grid_sizer, 0, wxEXPAND);
|
||||
|
||||
auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _(L("Manifold")));
|
||||
auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _(L("Manifold")) + ":");
|
||||
info_manifold_text->SetFont(wxGetApp().small_font());
|
||||
info_manifold = new wxStaticText(parent, wxID_ANY, "");
|
||||
info_manifold->SetFont(wxGetApp().small_font());
|
||||
|
|
@ -138,6 +145,8 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
|
|||
sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2);
|
||||
sizer_manifold->Add(info_manifold, 0, wxLEFT, 2);
|
||||
Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4);
|
||||
|
||||
sla_hidden_items = { label_volume, info_volume, label_materials, info_materials };
|
||||
}
|
||||
|
||||
void ObjectInfo::show_sizer(bool show)
|
||||
|
|
@ -152,6 +161,7 @@ enum SlisedInfoIdx
|
|||
siFilament_m,
|
||||
siFilament_mm3,
|
||||
siFilament_g,
|
||||
siMateril_unit,
|
||||
siCost,
|
||||
siEstimatedTime,
|
||||
siWTNumbetOfToolchanges,
|
||||
|
|
@ -192,6 +202,7 @@ SlicedInfo::SlicedInfo(wxWindow *parent) :
|
|||
init_info_label(_(L("Used Filament (m)")));
|
||||
init_info_label(_(L("Used Filament (mm³)")));
|
||||
init_info_label(_(L("Used Filament (g)")));
|
||||
init_info_label(_(L("Used Material (unit)")));
|
||||
init_info_label(_(L("Cost")));
|
||||
init_info_label(_(L("Estimated printing time")));
|
||||
init_info_label(_(L("Number of tool changes")));
|
||||
|
|
@ -212,7 +223,7 @@ void SlicedInfo::SetTextAndShow(SlisedInfoIdx idx, const wxString& text, const w
|
|||
}
|
||||
|
||||
PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) :
|
||||
wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(200,-1), 0, nullptr, wxCB_READONLY),
|
||||
wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 * wxGetApp().em_unit(), -1), 0, nullptr, wxCB_READONLY),
|
||||
preset_type(preset_type),
|
||||
last_selected(wxNOT_FOUND)
|
||||
{
|
||||
|
|
@ -305,7 +316,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
|
|||
|
||||
// Frequently changed parameters for FFF_technology
|
||||
m_og->set_config(config);
|
||||
m_og->label_width = label_width;
|
||||
m_og->label_width = label_width == 0 ? 1 : label_width;
|
||||
|
||||
m_og->m_on_change = [config, this](t_config_option_key opt_key, boost::any value) {
|
||||
Tab* tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT);
|
||||
|
|
@ -350,29 +361,35 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
|
|||
tab_print->update_dirty();
|
||||
};
|
||||
|
||||
Option option = m_og->get_option("fill_density");
|
||||
option.opt.sidetext = "";
|
||||
option.opt.full_width = true;
|
||||
m_og->append_single_option_line(option);
|
||||
|
||||
Line line = Line { "", "" };
|
||||
|
||||
ConfigOptionDef def;
|
||||
|
||||
def.label = L("Support");
|
||||
def.label = L("Supports");
|
||||
def.type = coStrings;
|
||||
def.gui_type = "select_open";
|
||||
def.tooltip = L("Select what kind of support do you need");
|
||||
def.enum_labels.push_back(L("None"));
|
||||
def.enum_labels.push_back(L("Support on build plate only"));
|
||||
def.enum_labels.push_back(L("Everywhere"));
|
||||
std::string selection = !config->opt_bool("support_material") ?
|
||||
"None" :
|
||||
config->opt_bool("support_material_buildplate_only") ?
|
||||
const std::string selection = !config->opt_bool("support_material") ?
|
||||
"None" : config->opt_bool("support_material_buildplate_only") ?
|
||||
"Support on build plate only" :
|
||||
"Everywhere";
|
||||
def.default_value = new ConfigOptionStrings{ selection };
|
||||
option = Option(def, "support");
|
||||
Option option = Option(def, "support");
|
||||
option.opt.full_width = true;
|
||||
m_og->append_single_option_line(option);
|
||||
line.append_option(option);
|
||||
m_og->append_line(line);
|
||||
|
||||
|
||||
line = Line { "", "" };
|
||||
|
||||
option = m_og->get_option("fill_density");
|
||||
option.opt.label = L("Infill");
|
||||
option.opt.width = 7 * wxGetApp().em_unit();
|
||||
option.opt.sidetext = " ";
|
||||
line.append_option(option);
|
||||
|
||||
m_brim_width = config->opt_float("brim_width");
|
||||
def.label = L("Brim");
|
||||
|
|
@ -381,11 +398,10 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
|
|||
def.gui_type = "";
|
||||
def.default_value = new ConfigOptionBool{ m_brim_width > 0.0 ? true : false };
|
||||
option = Option(def, "brim");
|
||||
m_og->append_single_option_line(option);
|
||||
option.opt.sidetext = " ";
|
||||
line.append_option(option);
|
||||
|
||||
|
||||
Line line = { "", "" };
|
||||
line.widget = [config, this](wxWindow* parent) {
|
||||
auto wiping_dialog_btn = [config, this](wxWindow* parent) {
|
||||
m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
|
||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(m_wiping_dialog_button);
|
||||
|
|
@ -407,14 +423,15 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
|
|||
}));
|
||||
return sizer;
|
||||
};
|
||||
m_og->append_line(line);
|
||||
line.append_widget(wiping_dialog_btn);
|
||||
|
||||
m_og->append_line(line);
|
||||
|
||||
// Frequently changed parameters for SLA_technology
|
||||
m_og_sla = std::make_shared<ConfigOptionsGroup>(parent, "");
|
||||
DynamicPrintConfig* config_sla = &wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
m_og_sla->set_config(config_sla);
|
||||
m_og_sla->label_width = label_width*2;
|
||||
m_og_sla->label_width = label_width == 0 ? 1 : label_width;
|
||||
|
||||
m_og_sla->m_on_change = [config_sla, this](t_config_option_key opt_key, boost::any value) {
|
||||
Tab* tab = wxGetApp().get_tab(Preset::TYPE_SLA_PRINT);
|
||||
|
|
@ -428,12 +445,22 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
|
|||
tab->update_dirty();
|
||||
};
|
||||
|
||||
m_og_sla->append_single_option_line("supports_enable");
|
||||
m_og_sla->append_single_option_line("pad_enable");
|
||||
|
||||
line = Line{ "", "" };
|
||||
|
||||
option = m_og_sla->get_option("supports_enable");
|
||||
option.opt.sidetext = " ";
|
||||
line.append_option(option);
|
||||
|
||||
option = m_og_sla->get_option("pad_enable");
|
||||
option.opt.sidetext = " ";
|
||||
line.append_option(option);
|
||||
|
||||
m_og_sla->append_line(line);
|
||||
|
||||
m_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_sizer->Add(m_og->sizer, 0, wxEXPAND);
|
||||
m_sizer->Add(m_og_sla->sizer, 0, wxEXPAND | wxTOP, 5);
|
||||
m_sizer->Add(m_og_sla->sizer, 0, wxEXPAND);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -494,7 +521,7 @@ struct Sidebar::priv
|
|||
|
||||
void Sidebar::priv::show_preset_comboboxes()
|
||||
{
|
||||
const bool showSLA = plater->printer_technology() == ptSLA;
|
||||
const bool showSLA = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA;
|
||||
|
||||
wxWindowUpdateLocker noUpdates_scrolled(scrolled->GetParent());
|
||||
|
||||
|
|
@ -518,7 +545,7 @@ void Sidebar::priv::show_preset_comboboxes()
|
|||
Sidebar::Sidebar(Plater *parent)
|
||||
: wxPanel(parent), p(new priv(parent))
|
||||
{
|
||||
p->scrolled = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(400, -1));
|
||||
p->scrolled = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(40 * wxGetApp().em_unit(), -1));
|
||||
p->scrolled->SetScrollbars(0, 20, 1, 2);
|
||||
|
||||
// Sizer in the scrolled area
|
||||
|
|
@ -526,26 +553,26 @@ Sidebar::Sidebar(Plater *parent)
|
|||
p->scrolled->SetSizer(scrolled_sizer);
|
||||
|
||||
// Sizer with buttons for mode changing
|
||||
p->mode_sizer = new PrusaModeSizer(p->scrolled);
|
||||
p->mode_sizer = new PrusaModeSizer(p->scrolled, 2 * wxGetApp().em_unit());
|
||||
|
||||
// The preset chooser
|
||||
p->sizer_presets = new wxFlexGridSizer(5, 2, 1, 2);
|
||||
p->sizer_presets->AddGrowableCol(1, 1);
|
||||
p->sizer_presets = new wxFlexGridSizer(10, 1, 1, 2);
|
||||
p->sizer_presets->AddGrowableCol(0, 1);
|
||||
p->sizer_presets->SetFlexibleDirection(wxBOTH);
|
||||
p->sizer_filaments = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto init_combo = [this](PresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) {
|
||||
auto *text = new wxStaticText(p->scrolled, wxID_ANY, label);
|
||||
auto *text = new wxStaticText(p->scrolled, wxID_ANY, label+" :");
|
||||
text->SetFont(wxGetApp().small_font());
|
||||
*combo = new PresetComboBox(p->scrolled, preset_type);
|
||||
|
||||
auto *sizer_presets = this->p->sizer_presets;
|
||||
auto *sizer_filaments = this->p->sizer_filaments;
|
||||
sizer_presets->Add(text, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL | wxRIGHT, 4);
|
||||
sizer_presets->Add(text, 0, wxALIGN_LEFT | wxEXPAND | wxRIGHT, 4);
|
||||
if (! filament) {
|
||||
sizer_presets->Add(*combo, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxBOTTOM, 1);
|
||||
sizer_presets->Add(*combo, 0, wxEXPAND | wxBOTTOM, 1);
|
||||
} else {
|
||||
sizer_filaments->Add(*combo, 1, wxEXPAND | wxBOTTOM, 1);
|
||||
sizer_filaments->Add(*combo, 0, wxEXPAND | wxBOTTOM, 1);
|
||||
(*combo)->set_extruder_idx(0);
|
||||
sizer_presets->Add(sizer_filaments, 1, wxEXPAND);
|
||||
}
|
||||
|
|
@ -559,29 +586,32 @@ Sidebar::Sidebar(Plater *parent)
|
|||
init_combo(&p->combo_printer, _(L("Printer")), Preset::TYPE_PRINTER, false);
|
||||
|
||||
// calculate width of the preset labels
|
||||
p->sizer_presets->Layout();
|
||||
const wxArrayInt& ar = p->sizer_presets->GetColWidths();
|
||||
int label_width = ar.IsEmpty() ? 100 : ar.front()-4;
|
||||
// p->sizer_presets->Layout();
|
||||
// const wxArrayInt& ar = p->sizer_presets->GetColWidths();
|
||||
// const int label_width = ar.IsEmpty() ? 10*wxGetApp().em_unit() : ar.front()-4;
|
||||
|
||||
const int margin_5 = int(0.5*wxGetApp().em_unit());// 5;
|
||||
const int margin_10 = int(1.5*wxGetApp().em_unit());// 15;
|
||||
|
||||
p->sizer_params = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Frequently changed parameters
|
||||
p->frequently_changed_parameters = new FreqChangedParams(p->scrolled, label_width);
|
||||
p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxBOTTOM | wxLEFT, 2);
|
||||
p->frequently_changed_parameters = new FreqChangedParams(p->scrolled, 0/*label_width*/);
|
||||
p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM, margin_10);
|
||||
|
||||
// Object List
|
||||
p->object_list = new ObjectList(p->scrolled);
|
||||
p->sizer_params->Add(p->object_list->get_sizer(), 1, wxEXPAND | wxTOP, 20);
|
||||
p->sizer_params->Add(p->object_list->get_sizer(), 1, wxEXPAND);
|
||||
|
||||
// Object Manipulations
|
||||
p->object_manipulation = new ObjectManipulation(p->scrolled);
|
||||
p->object_manipulation->Hide();
|
||||
p->sizer_params->Add(p->object_manipulation->get_sizer(), 0, wxEXPAND | wxLEFT | wxTOP, 20);
|
||||
p->sizer_params->Add(p->object_manipulation->get_sizer(), 0, wxEXPAND | wxTOP, margin_5);
|
||||
|
||||
// Frequently Object Settings
|
||||
p->object_settings = new ObjectSettings(p->scrolled);
|
||||
p->object_settings->Hide();
|
||||
p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxLEFT | wxTOP, 20);
|
||||
p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxTOP, margin_5);
|
||||
|
||||
wxBitmap arrow_up(GUI::from_u8(Slic3r::var("brick_go.png")), wxBITMAP_TYPE_PNG);
|
||||
p->btn_send_gcode = new wxButton(this, wxID_ANY, _(L("Send to printer")));
|
||||
|
|
@ -594,11 +624,11 @@ Sidebar::Sidebar(Plater *parent)
|
|||
p->sliced_info = new SlicedInfo(p->scrolled);
|
||||
|
||||
// Sizer in the scrolled area
|
||||
scrolled_sizer->Add(p->mode_sizer, 0, wxALIGN_RIGHT/*CENTER_HORIZONTAL*/ | wxBOTTOM | wxRIGHT, 5);
|
||||
scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | wxLEFT, 2);
|
||||
scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND);
|
||||
scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | wxLEFT, 20);
|
||||
scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | wxLEFT, 20);
|
||||
scrolled_sizer->Add(p->mode_sizer, 0, wxALIGN_CENTER_HORIZONTAL/*RIGHT | wxBOTTOM | wxRIGHT, 5*/);
|
||||
scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | wxLEFT, margin_5);
|
||||
scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND | wxLEFT, margin_5);
|
||||
scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | wxLEFT, margin_5);
|
||||
scrolled_sizer->Add(p->sliced_info, 0, wxEXPAND | wxTOP | wxLEFT, margin_5);
|
||||
|
||||
// Buttons underneath the scrolled area
|
||||
p->btn_export_gcode = new wxButton(this, wxID_ANY, _(L("Export G-code")) + dots);
|
||||
|
|
@ -608,13 +638,13 @@ Sidebar::Sidebar(Plater *parent)
|
|||
enable_buttons(false);
|
||||
|
||||
auto *btns_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, 5);
|
||||
btns_sizer->Add(p->btn_send_gcode, 0, wxEXPAND | wxTOP, 5);
|
||||
btns_sizer->Add(p->btn_export_gcode, 0, wxEXPAND | wxTOP, 5);
|
||||
btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5);
|
||||
btns_sizer->Add(p->btn_send_gcode, 0, wxEXPAND | wxTOP, margin_5);
|
||||
btns_sizer->Add(p->btn_export_gcode, 0, wxEXPAND | wxTOP, margin_5);
|
||||
|
||||
auto *sizer = new wxBoxSizer(wxVERTICAL);
|
||||
sizer->Add(p->scrolled, 1, wxEXPAND | wxTOP, 5);
|
||||
sizer->Add(btns_sizer, 0, wxEXPAND | wxLEFT, 20);
|
||||
sizer->Add(p->scrolled, 1, wxEXPAND);
|
||||
sizer->Add(btns_sizer, 0, wxEXPAND | wxLEFT, margin_5);
|
||||
SetSizer(sizer);
|
||||
|
||||
// Events
|
||||
|
|
@ -652,11 +682,12 @@ void Sidebar::remove_unused_filament_combos(const int current_extruder_count)
|
|||
void Sidebar::update_presets(Preset::Type preset_type)
|
||||
{
|
||||
PresetBundle &preset_bundle = *wxGetApp().preset_bundle;
|
||||
const auto print_tech = preset_bundle.printers.get_edited_preset().printer_technology();
|
||||
|
||||
switch (preset_type) {
|
||||
case Preset::TYPE_FILAMENT:
|
||||
{
|
||||
const int extruder_cnt = p->plater->printer_technology() != ptFFF ? 1 :
|
||||
const int extruder_cnt = print_tech != ptFFF ? 1 :
|
||||
dynamic_cast<ConfigOptionFloats*>(preset_bundle.printers.get_edited_preset().config.option("nozzle_diameter"))->values.size();
|
||||
const int filament_cnt = p->combos_filament.size() > extruder_cnt ? extruder_cnt : p->combos_filament.size();
|
||||
|
||||
|
|
@ -688,7 +719,7 @@ void Sidebar::update_presets(Preset::Type preset_type)
|
|||
case Preset::TYPE_PRINTER:
|
||||
{
|
||||
// Update the print choosers to only contain the compatible presets, update the dirty flags.
|
||||
if (p->plater->printer_technology() == ptFFF)
|
||||
if (print_tech == ptFFF)
|
||||
preset_bundle.prints.update_platter_ui(p->combo_print);
|
||||
else {
|
||||
preset_bundle.sla_prints.update_platter_ui(p->combo_sla_print);
|
||||
|
|
@ -701,7 +732,7 @@ void Sidebar::update_presets(Preset::Type preset_type)
|
|||
p->combo_printer->check_selection();
|
||||
// Update the filament choosers to only contain the compatible presets, update the color preview,
|
||||
// update the dirty flags.
|
||||
if (p->plater->printer_technology() == ptFFF) {
|
||||
if (print_tech == ptFFF) {
|
||||
for (size_t i = 0; i < p->combos_filament.size(); ++ i)
|
||||
preset_bundle.update_platter_filament_ui(i, p->combos_filament[i]);
|
||||
}
|
||||
|
|
@ -808,6 +839,11 @@ void Sidebar::show_info_sizer()
|
|||
}
|
||||
|
||||
p->object_info->show_sizer(true);
|
||||
|
||||
if (p->plater->printer_technology() == ptSLA) {
|
||||
for (auto item: p->object_info->sla_hidden_items)
|
||||
item->Show(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Sidebar::show_sliced_info_sizer(const bool show)
|
||||
|
|
@ -816,6 +852,32 @@ void Sidebar::show_sliced_info_sizer(const bool show)
|
|||
|
||||
p->sliced_info->Show(show);
|
||||
if (show) {
|
||||
if (p->plater->printer_technology() == ptSLA)
|
||||
{
|
||||
const SLAPrintStatistics& ps = p->plater->sla_print().print_statistics();
|
||||
wxString new_label = _(L("Used Material (ml)")) + " :";
|
||||
const bool is_supports = ps.support_used_material > 0.0;
|
||||
if (is_supports)
|
||||
new_label += wxString::Format("\n - %s\n - %s", _(L("object(s)")), _(L("supports and pad")));
|
||||
|
||||
wxString info_text = is_supports ?
|
||||
wxString::Format("%.2f \n%.2f \n%.2f", (ps.objects_used_material + ps.support_used_material) / 1000,
|
||||
ps.objects_used_material / 1000,
|
||||
ps.support_used_material / 1000) :
|
||||
wxString::Format("%.2f", (ps.objects_used_material + ps.support_used_material) / 1000);
|
||||
p->sliced_info->SetTextAndShow(siMateril_unit, info_text, new_label);
|
||||
|
||||
p->sliced_info->SetTextAndShow(siCost, "N/A"/*wxString::Format("%.2f", ps.total_cost)*/);
|
||||
p->sliced_info->SetTextAndShow(siEstimatedTime, ps.estimated_print_time, _(L("Estimated printing time")) + " :");
|
||||
|
||||
// Hide non-SLA sliced info parameters
|
||||
p->sliced_info->SetTextAndShow(siFilament_m, "N/A");
|
||||
p->sliced_info->SetTextAndShow(siFilament_mm3, "N/A");
|
||||
p->sliced_info->SetTextAndShow(siFilament_g, "N/A");
|
||||
p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, "N/A");
|
||||
}
|
||||
else
|
||||
{
|
||||
const PrintStatistics& ps = p->plater->fff_print().print_statistics();
|
||||
const bool is_wipe_tower = ps.total_wipe_tower_filament > 0;
|
||||
|
||||
|
|
@ -863,6 +925,10 @@ void Sidebar::show_sliced_info_sizer(const bool show)
|
|||
|
||||
// if there is a wipe tower, insert number of toolchanges info into the array:
|
||||
p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", p->plater->fff_print().wipe_tower_data().number_of_toolchanges) : "N/A");
|
||||
|
||||
// Hide non-FFF sliced info parameters
|
||||
p->sliced_info->SetTextAndShow(siMateril_unit, "N/A");
|
||||
}
|
||||
}
|
||||
|
||||
Layout();
|
||||
|
|
@ -952,6 +1018,7 @@ struct Plater::priv
|
|||
wxPanel* current_panel;
|
||||
std::vector<wxPanel*> panels;
|
||||
Sidebar *sidebar;
|
||||
Bed3D bed;
|
||||
View3D* view3D;
|
||||
GLToolbar view_toolbar;
|
||||
Preview *preview;
|
||||
|
|
@ -962,6 +1029,7 @@ struct Plater::priv
|
|||
std::atomic<bool> arranging;
|
||||
std::atomic<bool> rotoptimizing;
|
||||
bool delayed_scene_refresh;
|
||||
std::string delayed_error_message;
|
||||
|
||||
wxTimer background_process_timer;
|
||||
|
||||
|
|
@ -1055,6 +1123,12 @@ struct Plater::priv
|
|||
|
||||
void update_object_menu();
|
||||
|
||||
// Set the bed shape to a single closed 2D polygon(array of two element arrays),
|
||||
// triangulate the bed and store the triangles into m_bed.m_triangles,
|
||||
// fills the m_bed.m_grid_lines and sets m_bed.m_origin.
|
||||
// Sets m_bed.m_polygon to limit the object placement.
|
||||
void set_bed_shape(const Pointfs& shape);
|
||||
|
||||
private:
|
||||
bool init_object_menu();
|
||||
bool init_common_menu(wxMenu* menu, const bool is_part = false);
|
||||
|
|
@ -1126,9 +1200,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
|
||||
view3D = new View3D(q, &model, config, &background_process);
|
||||
preview = new Preview(q, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); });
|
||||
// Let the Tab key switch between the 3D view and the layer preview.
|
||||
view3D->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); });
|
||||
preview->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->select_next_view_3D(); });
|
||||
|
||||
view3D->set_bed(&bed);
|
||||
preview->set_bed(&bed);
|
||||
|
||||
panels.push_back(view3D);
|
||||
panels.push_back(preview);
|
||||
|
|
@ -1136,12 +1210,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
this->background_process_timer.SetOwner(this->q, 0);
|
||||
this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->update_restart_background_process(false, false); });
|
||||
|
||||
#if !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
auto *bed_shape = config->opt<ConfigOptionPoints>("bed_shape");
|
||||
view3D->set_bed_shape(bed_shape->values);
|
||||
preview->set_bed_shape(bed_shape->values);
|
||||
#endif // !ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
|
||||
update();
|
||||
|
||||
auto *hsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
|
@ -1181,6 +1249,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
view3D_canvas->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event<bool> &evt) { this->sidebar->enable_buttons(evt.data); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_GEOMETRY, &priv::on_update_geometry, this);
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this);
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
|
||||
// 3DScene/Toolbar:
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); });
|
||||
|
|
@ -1192,10 +1261,13 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this);
|
||||
view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this);
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); });
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values); });
|
||||
|
||||
// Preview events:
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this);
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&) { set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values); });
|
||||
preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); });
|
||||
|
||||
view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); });
|
||||
|
||||
|
|
@ -1920,6 +1992,7 @@ void Plater::priv::split_volume()
|
|||
|
||||
void Plater::priv::schedule_background_process()
|
||||
{
|
||||
delayed_error_message.clear();
|
||||
// Trigger the timer event after 0.5s
|
||||
this->background_process_timer.Start(500, wxTIMER_ONE_SHOT);
|
||||
// Notify the Canvas3D that something has changed, so it may invalidate some of the layer editing stuff.
|
||||
|
|
@ -1947,6 +2020,8 @@ unsigned int Plater::priv::update_background_process(bool force_validation)
|
|||
this->background_process_timer.Stop();
|
||||
// Update the "out of print bed" state of ModelInstances.
|
||||
this->update_print_volume_state();
|
||||
// The delayed error message is no more valid.
|
||||
this->delayed_error_message.clear();
|
||||
// Apply new config to the possibly running background task.
|
||||
bool was_running = this->background_process.running();
|
||||
Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config());
|
||||
|
|
@ -1985,8 +2060,18 @@ unsigned int Plater::priv::update_background_process(bool force_validation)
|
|||
return_state |= UPDATE_BACKGROUND_PROCESS_RESTART;
|
||||
} else {
|
||||
// The print is not valid.
|
||||
// Only show the error message immediately, if the top level parent of this window is active.
|
||||
auto p = dynamic_cast<wxWindow*>(this->q);
|
||||
while (p->GetParent())
|
||||
p = p->GetParent();
|
||||
auto *top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
|
||||
if (top_level_wnd && top_level_wnd->IsActive()) {
|
||||
// The error returned from the Print needs to be translated into the local language.
|
||||
GUI::show_error(this->q, _(err));
|
||||
} else {
|
||||
// Show the error message once the main window gets activated.
|
||||
this->delayed_error_message = _(err);
|
||||
}
|
||||
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
|
||||
}
|
||||
}
|
||||
|
|
@ -2000,6 +2085,14 @@ unsigned int Plater::priv::update_background_process(bool force_validation)
|
|||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone());
|
||||
}
|
||||
|
||||
//FIXME update "Slice Now / Schedule background process"
|
||||
//background_process.is_export_scheduled() - byl zavolan "Export G-code", background processing ma jmeno export souboru
|
||||
//background_process.is_upload_scheduled() - byl zavolan "Send to OctoPrint", jeste nebylo doslajsovano (pak se preda upload fronte a background process zapomene)
|
||||
//background_process.empty() - prazdna plocha
|
||||
// pokud (return_state & UPDATE_BACKGROUND_PROCESS_INVALID) != 0 -> doslo k chybe (gray out "Slice now") mozna "Invalid data"???
|
||||
// jinak background_process.running() -> Zobraz "Slicing ..."
|
||||
// jinak pokud ! background_process.empty() && ! background_process.finished() -> je neco ke slajsovani (povol tlacitko) "Slice Now"
|
||||
|
||||
return return_state;
|
||||
}
|
||||
|
||||
|
|
@ -2049,6 +2142,8 @@ void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job)
|
|||
background_process.schedule_upload(std::move(upload_job));
|
||||
}
|
||||
|
||||
// If the SLA processing of just a single object's supports is running, restart slicing for the whole object.
|
||||
this->background_process.set_task(PrintBase::TaskParams());
|
||||
this->restart_background_process(priv::UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT);
|
||||
}
|
||||
|
||||
|
|
@ -2231,6 +2326,10 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS) {
|
||||
// Update SLA gizmo (reload_scene calls update_gizmos_data)
|
||||
q->canvas3D()->reload_scene(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::priv::on_slicing_completed(wxCommandEvent &)
|
||||
|
|
@ -2352,8 +2451,15 @@ void Plater::priv::on_right_click(Vec2dEvent& evt)
|
|||
|
||||
sidebar->obj_list()->append_menu_item_settings(menu);
|
||||
|
||||
if (q != nullptr)
|
||||
if (q != nullptr) {
|
||||
#ifdef __linux__
|
||||
// For some reason on Linux the menu isn't displayed if position is specified
|
||||
// (even though the position is sane).
|
||||
q->PopupMenu(menu);
|
||||
#else
|
||||
q->PopupMenu(menu, (int)evt.data.x(), (int)evt.data.y());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::priv::on_wipetower_moved(Vec3dEvent &evt)
|
||||
|
|
@ -2630,6 +2736,16 @@ bool Plater::priv::can_mirror() const
|
|||
return get_selection().is_from_single_instance();
|
||||
}
|
||||
|
||||
void Plater::priv::set_bed_shape(const Pointfs& shape)
|
||||
{
|
||||
bool new_shape = bed.set_shape(shape);
|
||||
if (new_shape)
|
||||
{
|
||||
if (view3D) view3D->bed_shape_changed();
|
||||
if (preview) preview->bed_shape_changed();
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::priv::update_object_menu()
|
||||
{
|
||||
sidebar->obj_list()->append_menu_items_add_volume(&object_menu);
|
||||
|
|
@ -2884,6 +3000,8 @@ void Plater::export_stl(bool selection_only)
|
|||
const wxString path = dialog->GetPath();
|
||||
const std::string path_u8 = into_u8(path);
|
||||
|
||||
wxBusyCursor wait;
|
||||
|
||||
TriangleMesh mesh;
|
||||
if (selection_only) {
|
||||
const auto &selection = p->get_selection();
|
||||
|
|
@ -2959,10 +3077,38 @@ void Plater::reslice()
|
|||
unsigned int state = this->p->update_background_process(true);
|
||||
if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE)
|
||||
this->p->view3D->reload_scene(false);
|
||||
// If the SLA processing of just a single object's supports is running, restart slicing for the whole object.
|
||||
this->p->background_process.set_task(PrintBase::TaskParams());
|
||||
// Only restarts if the state is valid.
|
||||
this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART);
|
||||
}
|
||||
|
||||
void Plater::reslice_SLA_supports(const ModelObject &object)
|
||||
{
|
||||
//FIXME Don't reslice if export of G-code or sending to OctoPrint is running.
|
||||
// bitmask of UpdateBackgroundProcessReturnState
|
||||
unsigned int state = this->p->update_background_process(true);
|
||||
if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE)
|
||||
this->p->view3D->reload_scene(false);
|
||||
|
||||
if (this->p->background_process.empty() || (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID))
|
||||
// Nothing to do on empty input or invalid configuration.
|
||||
return;
|
||||
|
||||
// Limit calculation to the single object only.
|
||||
PrintBase::TaskParams task;
|
||||
task.single_model_object = object.id();
|
||||
// If the background processing is not enabled, calculate supports just for the single instance.
|
||||
// Otherwise calculate everything, but start with the provided object.
|
||||
if (!this->p->background_processing_enabled()) {
|
||||
task.single_model_instance_only = true;
|
||||
task.to_object_step = slaposBasePool;
|
||||
}
|
||||
this->p->background_process.set_task(task);
|
||||
// and let the background processing start.
|
||||
this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART);
|
||||
}
|
||||
|
||||
void Plater::send_gcode()
|
||||
{
|
||||
if (p->model.objects.empty()) { return; }
|
||||
|
|
@ -3023,20 +3169,13 @@ void Plater::on_extruders_change(int num_extruders)
|
|||
void Plater::on_config_change(const DynamicPrintConfig &config)
|
||||
{
|
||||
bool update_scheduled = false;
|
||||
#if ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
bool bed_shape_changed = false;
|
||||
#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
for (auto opt_key : p->config->diff(config)) {
|
||||
p->config->set_key_value(opt_key, config.option(opt_key)->clone());
|
||||
if (opt_key == "printer_technology")
|
||||
this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key));
|
||||
else if (opt_key == "bed_shape") {
|
||||
#if ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
bed_shape_changed = true;
|
||||
#else
|
||||
if (p->view3D) p->view3D->set_bed_shape(p->config->option<ConfigOptionPoints>(opt_key)->values);
|
||||
if (p->preview) p->preview->set_bed_shape(p->config->option<ConfigOptionPoints>(opt_key)->values);
|
||||
#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
update_scheduled = true;
|
||||
}
|
||||
else if (boost::starts_with(opt_key, "wipe_tower") ||
|
||||
|
|
@ -3062,12 +3201,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
|||
}
|
||||
else if (opt_key == "printer_model") {
|
||||
// update to force bed selection(for texturing)
|
||||
#if ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
bed_shape_changed = true;
|
||||
#else
|
||||
if (p->view3D) p->view3D->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values);
|
||||
if (p->preview) p->preview->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values);
|
||||
#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
update_scheduled = true;
|
||||
}
|
||||
else if (opt_key == "host_type" && this->p->printer_technology == ptSLA) {
|
||||
|
|
@ -3080,13 +3214,8 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
|||
p->sidebar->show_send(prin_host_opt != nullptr && !prin_host_opt->value.empty());
|
||||
}
|
||||
|
||||
#if ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
if (bed_shape_changed)
|
||||
{
|
||||
if (p->view3D) p->view3D->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values);
|
||||
if (p->preview) p->preview->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values);
|
||||
}
|
||||
#endif // ENABLE_REWORKED_BED_SHAPE_CHANGE
|
||||
p->set_bed_shape(p->config->option<ConfigOptionPoints>("bed_shape")->values);
|
||||
|
||||
if (update_scheduled)
|
||||
update();
|
||||
|
|
@ -3095,6 +3224,26 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
|
|||
this->p->schedule_background_process();
|
||||
}
|
||||
|
||||
void Plater::on_activate()
|
||||
{
|
||||
#ifdef __linux__
|
||||
wxWindow *focus_window = wxWindow::FindFocus();
|
||||
// Activating the main frame, and no window has keyboard focus.
|
||||
// Set the keyboard focus to the visible Canvas3D.
|
||||
if (this->p->view3D->IsShown() && (!focus_window || focus_window == this->p->view3D->get_wxglcanvas()))
|
||||
this->p->view3D->get_wxglcanvas()->SetFocus();
|
||||
|
||||
else if (this->p->preview->IsShown() && (!focus_window || focus_window == this->p->view3D->get_wxglcanvas()))
|
||||
this->p->preview->get_wxglcanvas()->SetFocus();
|
||||
#endif
|
||||
|
||||
if (! this->p->delayed_error_message.empty()) {
|
||||
std::string msg = std::move(this->p->delayed_error_message);
|
||||
this->p->delayed_error_message.clear();
|
||||
GUI::show_error(this, msg);
|
||||
}
|
||||
}
|
||||
|
||||
const wxString& Plater::get_project_filename() const
|
||||
{
|
||||
return p->project_filename;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
|
||||
#include "Preset.hpp"
|
||||
|
||||
#include "3DScene.hpp"
|
||||
#include "GLTexture.hpp"
|
||||
|
||||
class wxButton;
|
||||
class wxBoxSizer;
|
||||
class wxGLCanvas;
|
||||
|
|
@ -19,6 +22,7 @@ class wxString;
|
|||
namespace Slic3r {
|
||||
|
||||
class Model;
|
||||
class ModelObject;
|
||||
class Print;
|
||||
class SLAPrint;
|
||||
|
||||
|
|
@ -145,12 +149,15 @@ public:
|
|||
void export_amf();
|
||||
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
|
||||
void reslice();
|
||||
void reslice_SLA_supports(const ModelObject &object);
|
||||
void changed_object(int obj_idx);
|
||||
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
|
||||
void send_gcode();
|
||||
|
||||
void on_extruders_change(int extruders_count);
|
||||
void on_config_change(const DynamicPrintConfig &config);
|
||||
// On activating the parent window.
|
||||
void on_activate();
|
||||
|
||||
void update_object_menu();
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue