Merge remote-tracking branch 'remotes/prusa/master' into masterPE
@ -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++');
|
||||
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/supermerill/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("Slic3r++ Releases"), L('Open the Slic3r++ releases page in your browser'), sub {
|
||||
Wx::LaunchDefaultBrowser('http://github.com/supermerill/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++ github'), sub {
|
||||
Wx::LaunchDefaultBrowser('http://github.com/supermerill/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;
|
@ -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,566 +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->Append(L("Filament"));
|
||||
$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;
|
||||
$self->{filament_idx} = 6;
|
||||
|
||||
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->gcode_preview_data->type == $self->{filament_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 = $filament_colors[$i];
|
||||
$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;
|
1
resources/icons/bed/mk2.svg
Normal file
After Width: | Height: | Size: 61 KiB |
1
resources/icons/bed/mk3.svg
Normal file
After Width: | Height: | Size: 61 KiB |
1
resources/icons/bed/sl1.svg
Normal file
After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 58 KiB |
BIN
resources/icons/printers/PrusaResearch_MK2.5S.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
resources/icons/printers/PrusaResearch_MK2.5SMMU2S.png
Normal file
After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 65 KiB |
BIN
resources/icons/printers/PrusaResearch_MK3S.png
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
resources/icons/printers/PrusaResearch_MK3SMMU2S.png
Normal file
After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 55 KiB |
@ -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-26 10:38+0100\n"
|
||||
"Last-Translator: Oleksandra Iushchenko <yusanka@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: uk\n"
|
||||
@ -51,6 +51,12 @@ msgstr "Відстань координат 0,0 G-коду від нижньог
|
||||
msgid "Circular"
|
||||
msgstr "Круговий"
|
||||
|
||||
msgid "Switch to %s mode"
|
||||
msgstr "Перейти до режиму %s"
|
||||
|
||||
msgid "Current mode is %s"
|
||||
msgstr "Поточний режим - %s"
|
||||
|
||||
#: src/slic3r/GUI/BedShapeDialog.cpp:69 src/slic3r/GUI/ConfigWizard.cpp:92
|
||||
#: src/slic3r/GUI/ConfigWizard.cpp:456 src/slic3r/GUI/ConfigWizard.cpp:470
|
||||
#: src/slic3r/GUI/GUI_ObjectManipulation.cpp:204
|
||||
@ -624,48 +630,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"
|
||||
@ -730,11 +736,11 @@ msgstr "Преференції застосування"
|
||||
|
||||
#: src/slic3r/GUI/GUI_App.cpp:616
|
||||
msgid "Simple"
|
||||
msgstr ""
|
||||
msgstr "Простий"
|
||||
|
||||
#: src/slic3r/GUI/GUI_App.cpp:616
|
||||
msgid "Simple View Mode"
|
||||
msgstr ""
|
||||
msgstr "Простий режим перегляду"
|
||||
|
||||
#: src/slic3r/GUI/GUI_App.cpp:617 src/slic3r/GUI/GUI_ObjectList.cpp:39
|
||||
#: src/slic3r/GUI/Tab.cpp:948 src/slic3r/GUI/Tab.cpp:962
|
||||
@ -747,23 +753,23 @@ msgstr "Розширений"
|
||||
|
||||
#: src/slic3r/GUI/GUI_App.cpp:617
|
||||
msgid "Advanced View Mode"
|
||||
msgstr ""
|
||||
msgstr "Розширений режим перегляду"
|
||||
|
||||
#: src/slic3r/GUI/GUI_App.cpp:618
|
||||
msgid "Expert"
|
||||
msgstr ""
|
||||
msgstr "Експерт"
|
||||
|
||||
#: src/slic3r/GUI/GUI_App.cpp:618
|
||||
msgid "Expert View Mode"
|
||||
msgstr ""
|
||||
msgstr "Режим перегляду Експерт"
|
||||
|
||||
#: src/slic3r/GUI/GUI_App.cpp:620
|
||||
msgid "Mode"
|
||||
msgstr ""
|
||||
msgstr "Режим"
|
||||
|
||||
#: src/slic3r/GUI/GUI_App.cpp:620
|
||||
msgid "Slic3r View Mode"
|
||||
msgstr ""
|
||||
msgstr "Режим перегляду Slic3r'у"
|
||||
|
||||
#: src/slic3r/GUI/GUI_App.cpp:622
|
||||
msgid "Change Application &Language"
|
||||
@ -1752,7 +1758,7 @@ msgstr "Вид"
|
||||
|
||||
#: src/slic3r/GUI/MainFrame.cpp:445
|
||||
msgid "&Help"
|
||||
msgstr "Доромога"
|
||||
msgstr "Допомога"
|
||||
|
||||
#: src/slic3r/GUI/MainFrame.cpp:472
|
||||
msgid "Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):"
|
||||
@ -5064,11 +5070,11 @@ msgstr "Вертикальна відстань між об'єктом та ін
|
||||
|
||||
#: src/libslic3r/PrintConfig.cpp:1923
|
||||
msgid "soluble"
|
||||
msgstr ""
|
||||
msgstr "розчинний"
|
||||
|
||||
#: src/libslic3r/PrintConfig.cpp:1924
|
||||
msgid "detachable"
|
||||
msgstr ""
|
||||
msgstr "відривний"
|
||||
|
||||
#: src/libslic3r/PrintConfig.cpp:1929
|
||||
msgid "Enforce support for the first"
|
||||
@ -5636,7 +5642,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"
|
||||
@ -5672,7 +5678,7 @@ msgstr ""
|
||||
|
||||
#: src/libslic3r/PrintConfig.cpp:3050
|
||||
msgid "Help"
|
||||
msgstr ""
|
||||
msgstr "Допомога"
|
||||
|
||||
#: src/libslic3r/PrintConfig.cpp:3051
|
||||
msgid "Show this help."
|
||||
|
@ -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.
|
||||
@ -112,7 +143,7 @@ post_process =
|
||||
print_settings_id =
|
||||
raft_layers = 0
|
||||
resolution = 0
|
||||
seam_position = nearest
|
||||
seam_position = hidden
|
||||
single_extruder_multi_material_priming = 1
|
||||
skirts = 1
|
||||
skirt_distance = 2
|
||||
@ -204,6 +235,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
|
||||
@ -239,11 +271,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
|
||||
@ -255,14 +291,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
|
||||
@ -272,6 +347,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
|
||||
@ -281,19 +357,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
|
||||
@ -307,6 +387,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
|
||||
@ -319,6 +400,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
|
||||
@ -348,6 +430,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
|
||||
@ -361,11 +444,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
|
||||
@ -380,11 +465,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
|
||||
@ -392,14 +493,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
|
||||
@ -407,31 +509,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
|
||||
@ -458,6 +564,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
|
||||
@ -474,6 +581,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
|
||||
@ -486,7 +594,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
|
||||
@ -494,14 +617,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
|
||||
@ -509,38 +633,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
|
||||
@ -574,6 +704,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
|
||||
@ -583,10 +714,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
|
||||
@ -598,6 +731,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
|
||||
@ -609,38 +743,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
|
||||
@ -648,6 +789,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
|
||||
@ -655,6 +797,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
|
||||
@ -941,7 +1084,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
|
||||
@ -970,6 +1113,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*
|
||||
@ -1016,13 +1164,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*
|
||||
@ -1030,7 +1179,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]
|
||||
@ -1050,14 +1199,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*
|
||||
@ -1140,14 +1292,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*
|
||||
@ -1329,33 +1520,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
|
||||
@ -1462,10 +1686,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
|
||||
@ -1476,7 +1700,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
|
||||
@ -1488,12 +1712,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
|
||||
@ -1501,13 +1725,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
|
||||
@ -1520,7 +1744,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
|
||||
@ -1552,7 +1788,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]
|
||||
@ -1586,20 +1822,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
|
||||
@ -1629,9 +1930,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
|
||||
@ -1649,6 +1950,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
|
||||
@ -1658,7 +1971,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]
|
||||
@ -1679,6 +2004,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
@ -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);
|
||||
}
|
12
resources/shaders/printbed.vs
Normal file
@ -0,0 +1,12 @@
|
||||
#version 110
|
||||
|
||||
attribute vec4 v_position;
|
||||
attribute vec2 v_tex_coords;
|
||||
|
||||
varying vec2 tex_coords;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * v_position;
|
||||
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;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_definitions(-D_BSD_SOURCE -D_DEFAULT_SOURCE) # To enable various useful macros and functions on Unices
|
||||
remove_definitions(-D_UNICODE -DUNICODE)
|
||||
@ -68,18 +70,31 @@ set(AVRDUDE_SOURCES
|
||||
)
|
||||
if (MSVC)
|
||||
set(AVRDUDE_SOURCES ${AVRDUDE_SOURCES}
|
||||
windows/utf8.c
|
||||
windows/unistd.cpp
|
||||
windows/getopt.c
|
||||
)
|
||||
endif()
|
||||
add_library(avrdude STATIC ${AVRDUDE_SOURCES})
|
||||
|
||||
set(STANDALONE_SOURCES
|
||||
main-standalone.c
|
||||
add_executable(avrdude-conf-gen conf-generate.cpp)
|
||||
|
||||
# Config file embedding
|
||||
add_custom_command(
|
||||
DEPENDS avrdude-conf-gen ${CMAKE_CURRENT_SOURCE_DIR}/avrdude-slic3r.conf
|
||||
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/avrdude-slic3r.conf.h
|
||||
COMMAND $<TARGET_FILE:avrdude-conf-gen> avrdude-slic3r.conf avrdude_slic3r_conf > avrdude-slic3r.conf.h
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
add_executable(avrdude-slic3r ${STANDALONE_SOURCES})
|
||||
|
||||
add_custom_target(gen_conf_h
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/avrdude-slic3r.conf.h
|
||||
)
|
||||
|
||||
add_library(avrdude STATIC ${AVRDUDE_SOURCES})
|
||||
add_dependencies(avrdude gen_conf_h)
|
||||
|
||||
add_executable(avrdude-slic3r main-standalone.cpp)
|
||||
target_link_libraries(avrdude-slic3r avrdude)
|
||||
set_target_properties(avrdude-slic3r PROPERTIES EXCLUDE_FROM_ALL TRUE)
|
||||
|
||||
if (WIN32)
|
||||
target_compile_definitions(avrdude PRIVATE WIN32NATIVE=1)
|
||||
|
@ -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"
|
||||
|
@ -158,8 +158,10 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stk500_getsync(pgm) < 0)
|
||||
if (stk500_getsync(pgm) < 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
424
src/avrdude/avrdude-slic3r.conf
Normal file
@ -0,0 +1,424 @@
|
||||
|
||||
#
|
||||
# This is a basic minimal config file embedded into the avrdude-slic3r binary
|
||||
# so that it can work in a standalone manner.
|
||||
#
|
||||
# Only the bits useful for Prusa3D devices were copied over from avrdude.conf
|
||||
# If needed, more configuration can still be loaded into avrdude-slic3r
|
||||
# via the -C command-line option.
|
||||
#
|
||||
|
||||
|
||||
programmer
|
||||
id = "wiring";
|
||||
desc = "Wiring";
|
||||
type = "wiring";
|
||||
connection_type = serial;
|
||||
;
|
||||
|
||||
programmer
|
||||
id = "arduino";
|
||||
desc = "Arduino";
|
||||
type = "arduino";
|
||||
connection_type = serial;
|
||||
;
|
||||
|
||||
programmer
|
||||
id = "avr109";
|
||||
desc = "Atmel AppNote AVR109 Boot Loader";
|
||||
type = "butterfly";
|
||||
connection_type = serial;
|
||||
;
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
# ATmega2560
|
||||
#------------------------------------------------------------
|
||||
|
||||
part
|
||||
id = "m2560";
|
||||
desc = "ATmega2560";
|
||||
signature = 0x1e 0x98 0x01;
|
||||
has_jtag = yes;
|
||||
stk500_devcode = 0xB2;
|
||||
# avr910_devcode = 0x43;
|
||||
chip_erase_delay = 9000;
|
||||
pagel = 0xD7;
|
||||
bs2 = 0xA0;
|
||||
reset = dedicated;
|
||||
pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1",
|
||||
"x x x x x x x x x x x x x x x x";
|
||||
|
||||
chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0",
|
||||
"x x x x x x x x x x x x x x x x";
|
||||
|
||||
timeout = 200;
|
||||
stabdelay = 100;
|
||||
cmdexedelay = 25;
|
||||
synchloops = 32;
|
||||
bytedelay = 0;
|
||||
pollindex = 3;
|
||||
pollvalue = 0x53;
|
||||
predelay = 1;
|
||||
postdelay = 1;
|
||||
pollmethod = 1;
|
||||
|
||||
pp_controlstack =
|
||||
0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F,
|
||||
0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F,
|
||||
0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B,
|
||||
0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02;
|
||||
hventerstabdelay = 100;
|
||||
progmodedelay = 0;
|
||||
latchcycles = 5;
|
||||
togglevtg = 1;
|
||||
poweroffdelay = 15;
|
||||
resetdelayms = 1;
|
||||
resetdelayus = 0;
|
||||
hvleavestabdelay = 15;
|
||||
chiperasepulsewidth = 0;
|
||||
chiperasepolltimeout = 10;
|
||||
programfusepulsewidth = 0;
|
||||
programfusepolltimeout = 5;
|
||||
programlockpulsewidth = 0;
|
||||
programlockpolltimeout = 5;
|
||||
|
||||
idr = 0x31;
|
||||
spmcr = 0x57;
|
||||
rampz = 0x3b;
|
||||
allowfullpagebitstream = no;
|
||||
|
||||
ocdrev = 4;
|
||||
|
||||
memory "eeprom"
|
||||
paged = no; /* leave this "no" */
|
||||
page_size = 8; /* for parallel programming */
|
||||
size = 4096;
|
||||
min_write_delay = 9000;
|
||||
max_write_delay = 9000;
|
||||
readback_p1 = 0x00;
|
||||
readback_p2 = 0x00;
|
||||
read = " 1 0 1 0 0 0 0 0",
|
||||
" x x x x a11 a10 a9 a8",
|
||||
" a7 a6 a5 a4 a3 a2 a1 a0",
|
||||
" o o o o o o o o";
|
||||
|
||||
write = " 1 1 0 0 0 0 0 0",
|
||||
" x x x x a11 a10 a9 a8",
|
||||
" a7 a6 a5 a4 a3 a2 a1 a0",
|
||||
" i i i i i i i i";
|
||||
|
||||
loadpage_lo = " 1 1 0 0 0 0 0 1",
|
||||
" 0 0 0 0 0 0 0 0",
|
||||
" 0 0 0 0 0 a2 a1 a0",
|
||||
" i i i i i i i i";
|
||||
|
||||
writepage = " 1 1 0 0 0 0 1 0",
|
||||
" 0 0 x x a11 a10 a9 a8",
|
||||
" a7 a6 a5 a4 a3 0 0 0",
|
||||
" x x x x x x x x";
|
||||
|
||||
mode = 0x41;
|
||||
delay = 10;
|
||||
blocksize = 8;
|
||||
readsize = 256;
|
||||
;
|
||||
|
||||
memory "flash"
|
||||
paged = yes;
|
||||
size = 262144;
|
||||
page_size = 256;
|
||||
num_pages = 1024;
|
||||
min_write_delay = 4500;
|
||||
max_write_delay = 4500;
|
||||
readback_p1 = 0x00;
|
||||
readback_p2 = 0x00;
|
||||
read_lo = " 0 0 1 0 0 0 0 0",
|
||||
"a15 a14 a13 a12 a11 a10 a9 a8",
|
||||
" a7 a6 a5 a4 a3 a2 a1 a0",
|
||||
" o o o o o o o o";
|
||||
|
||||
read_hi = " 0 0 1 0 1 0 0 0",
|
||||
"a15 a14 a13 a12 a11 a10 a9 a8",
|
||||
" a7 a6 a5 a4 a3 a2 a1 a0",
|
||||
" o o o o o o o o";
|
||||
|
||||
loadpage_lo = " 0 1 0 0 0 0 0 0",
|
||||
" x x x x x x x x",
|
||||
" x a6 a5 a4 a3 a2 a1 a0",
|
||||
" i i i i i i i i";
|
||||
|
||||
loadpage_hi = " 0 1 0 0 1 0 0 0",
|
||||
" x x x x x x x x",
|
||||
" x a6 a5 a4 a3 a2 a1 a0",
|
||||
" i i i i i i i i";
|
||||
|
||||
writepage = " 0 1 0 0 1 1 0 0",
|
||||
"a15 a14 a13 a12 a11 a10 a9 a8",
|
||||
" a7 x x x x x x x",
|
||||
" x x x x x x x x";
|
||||
|
||||
load_ext_addr = " 0 1 0 0 1 1 0 1",
|
||||
" 0 0 0 0 0 0 0 0",
|
||||
" 0 0 0 0 0 0 0 a16",
|
||||
" 0 0 0 0 0 0 0 0";
|
||||
|
||||
mode = 0x41;
|
||||
delay = 10;
|
||||
blocksize = 256;
|
||||
readsize = 256;
|
||||
;
|
||||
|
||||
memory "lfuse"
|
||||
size = 1;
|
||||
write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0",
|
||||
"x x x x x x x x i i i i i i i i";
|
||||
|
||||
read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||
"x x x x x x x x o o o o o o o o";
|
||||
min_write_delay = 9000;
|
||||
max_write_delay = 9000;
|
||||
;
|
||||
|
||||
memory "hfuse"
|
||||
size = 1;
|
||||
write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0",
|
||||
"x x x x x x x x i i i i i i i i";
|
||||
|
||||
read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0",
|
||||
"x x x x x x x x o o o o o o o o";
|
||||
min_write_delay = 9000;
|
||||
max_write_delay = 9000;
|
||||
;
|
||||
|
||||
memory "efuse"
|
||||
size = 1;
|
||||
write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0",
|
||||
"x x x x x x x x x x x x x i i i";
|
||||
|
||||
read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0",
|
||||
"x x x x x x x x o o o o o o o o";
|
||||
min_write_delay = 9000;
|
||||
max_write_delay = 9000;
|
||||
;
|
||||
|
||||
memory "lock"
|
||||
size = 1;
|
||||
read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0",
|
||||
"x x x x x x x x x x o o o o o o";
|
||||
|
||||
write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x",
|
||||
"x x x x x x x x 1 1 i i i i i i";
|
||||
min_write_delay = 9000;
|
||||
max_write_delay = 9000;
|
||||
;
|
||||
|
||||
memory "calibration"
|
||||
size = 1;
|
||||
read = "0 0 1 1 1 0 0 0 x x x x x x x x",
|
||||
"0 0 0 0 0 0 0 0 o o o o o o o o";
|
||||
;
|
||||
|
||||
memory "signature"
|
||||
size = 3;
|
||||
read = "0 0 1 1 0 0 0 0 x x x x x x x x",
|
||||
"x x x x x x a1 a0 o o o o o o o o";
|
||||
;
|
||||
;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#------------------------------------------------------------
|
||||
# ATmega32u4
|
||||
#------------------------------------------------------------
|
||||
|
||||
part
|
||||
id = "m32u4";
|
||||
desc = "ATmega32U4";
|
||||
signature = 0x1e 0x95 0x87;
|
||||
usbpid = 0x2ff4;
|
||||
has_jtag = yes;
|
||||
# stk500_devcode = 0xB2;
|
||||
# avr910_devcode = 0x43;
|
||||
chip_erase_delay = 9000;
|
||||
pagel = 0xD7;
|
||||
bs2 = 0xA0;
|
||||
reset = dedicated;
|
||||
pgm_enable = "1 0 1 0 1 1 0 0 0 1 0 1 0 0 1 1",
|
||||
"x x x x x x x x x x x x x x x x";
|
||||
|
||||
chip_erase = "1 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0",
|
||||
"x x x x x x x x x x x x x x x x";
|
||||
|
||||
timeout = 200;
|
||||
stabdelay = 100;
|
||||
cmdexedelay = 25;
|
||||
synchloops = 32;
|
||||
bytedelay = 0;
|
||||
pollindex = 3;
|
||||
pollvalue = 0x53;
|
||||
predelay = 1;
|
||||
postdelay = 1;
|
||||
pollmethod = 1;
|
||||
|
||||
pp_controlstack =
|
||||
0x0E, 0x1E, 0x0F, 0x1F, 0x2E, 0x3E, 0x2F, 0x3F,
|
||||
0x4E, 0x5E, 0x4F, 0x5F, 0x6E, 0x7E, 0x6F, 0x7F,
|
||||
0x66, 0x76, 0x67, 0x77, 0x6A, 0x7A, 0x6B, 0x7B,
|
||||
0xBE, 0xFD, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00;
|
||||
hventerstabdelay = 100;
|
||||
progmodedelay = 0;
|
||||
latchcycles = 5;
|
||||
togglevtg = 1;
|
||||
poweroffdelay = 15;
|
||||
resetdelayms = 1;
|
||||
resetdelayus = 0;
|
||||
hvleavestabdelay = 15;
|
||||
chiperasepulsewidth = 0;
|
||||
chiperasepolltimeout = 10;
|
||||
programfusepulsewidth = 0;
|
||||
programfusepolltimeout = 5;
|
||||
programlockpulsewidth = 0;
|
||||
programlockpolltimeout = 5;
|
||||
|
||||
idr = 0x31;
|
||||
spmcr = 0x57;
|
||||
rampz = 0x3b;
|
||||
allowfullpagebitstream = no;
|
||||
|
||||
ocdrev = 3;
|
||||
|
||||
memory "eeprom"
|
||||
paged = no; /* leave this "no" */
|
||||
page_size = 4; /* for parallel programming */
|
||||
size = 1024;
|
||||
min_write_delay = 9000;
|
||||
max_write_delay = 9000;
|
||||
readback_p1 = 0x00;
|
||||
readback_p2 = 0x00;
|
||||
read = " 1 0 1 0 0 0 0 0",
|
||||
" x x x x x a10 a9 a8",
|
||||
" a7 a6 a5 a4 a3 a2 a1 a0",
|
||||
" o o o o o o o o";
|
||||
|
||||
write = " 1 1 0 0 0 0 0 0",
|
||||
" x x x x x a10 a9 a8",
|
||||
" a7 a6 a5 a4 a3 a2 a1 a0",
|
||||
" i i i i i i i i";
|
||||
|
||||
loadpage_lo = " 1 1 0 0 0 0 0 1",
|
||||
" 0 0 0 0 0 0 0 0",
|
||||
" 0 0 0 0 0 a2 a1 a0",
|
||||
" i i i i i i i i";
|
||||
|
||||
writepage = " 1 1 0 0 0 0 1 0",
|
||||
" 0 0 x x x a10 a9 a8",
|
||||
" a7 a6 a5 a4 a3 0 0 0",
|
||||
" x x x x x x x x";
|
||||
|
||||
mode = 0x41;
|
||||
delay = 20;
|
||||
blocksize = 4;
|
||||
readsize = 256;
|
||||
;
|
||||
|
||||
memory "flash"
|
||||
paged = yes;
|
||||
size = 32768;
|
||||
page_size = 128;
|
||||
num_pages = 256;
|
||||
min_write_delay = 4500;
|
||||
max_write_delay = 4500;
|
||||
readback_p1 = 0x00;
|
||||
readback_p2 = 0x00;
|
||||
read_lo = " 0 0 1 0 0 0 0 0",
|
||||
" 0 a14 a13 a12 a11 a10 a9 a8",
|
||||
" a7 a6 a5 a4 a3 a2 a1 a0",
|
||||
" o o o o o o o o";
|
||||
|
||||
read_hi = " 0 0 1 0 1 0 0 0",
|
||||
" 0 a14 a13 a12 a11 a10 a9 a8",
|
||||
" a7 a6 a5 a4 a3 a2 a1 a0",
|
||||
" o o o o o o o o";
|
||||
|
||||
loadpage_lo = " 0 1 0 0 0 0 0 0",
|
||||
" x x x x x x x x",
|
||||
" x x a5 a4 a3 a2 a1 a0",
|
||||
" i i i i i i i i";
|
||||
|
||||
loadpage_hi = " 0 1 0 0 1 0 0 0",
|
||||
" x x x x x x x x",
|
||||
" x x a5 a4 a3 a2 a1 a0",
|
||||
" i i i i i i i i";
|
||||
|
||||
writepage = " 0 1 0 0 1 1 0 0",
|
||||
" a15 a14 a13 a12 a11 a10 a9 a8",
|
||||
" a7 a6 x x x x x x",
|
||||
" x x x x x x x x";
|
||||
|
||||
mode = 0x41;
|
||||
delay = 6;
|
||||
blocksize = 128;
|
||||
readsize = 256;
|
||||
;
|
||||
|
||||
memory "lfuse"
|
||||
size = 1;
|
||||
write = "1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 0",
|
||||
"x x x x x x x x i i i i i i i i";
|
||||
|
||||
read = "0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0",
|
||||
"x x x x x x x x o o o o o o o o";
|
||||
min_write_delay = 9000;
|
||||
max_write_delay = 9000;
|
||||
;
|
||||
|
||||
memory "hfuse"
|
||||
size = 1;
|
||||
write = "1 0 1 0 1 1 0 0 1 0 1 0 1 0 0 0",
|
||||
"x x x x x x x x i i i i i i i i";
|
||||
|
||||
read = "0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0",
|
||||
"x x x x x x x x o o o o o o o o";
|
||||
min_write_delay = 9000;
|
||||
max_write_delay = 9000;
|
||||
;
|
||||
|
||||
memory "efuse"
|
||||
size = 1;
|
||||
write = "1 0 1 0 1 1 0 0 1 0 1 0 0 1 0 0",
|
||||
"x x x x x x x x x x x x i i i i";
|
||||
|
||||
read = "0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0",
|
||||
"x x x x x x x x o o o o o o o o";
|
||||
min_write_delay = 9000;
|
||||
max_write_delay = 9000;
|
||||
;
|
||||
|
||||
memory "lock"
|
||||
size = 1;
|
||||
read = "0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0",
|
||||
"x x x x x x x x x x o o o o o o";
|
||||
|
||||
write = "1 0 1 0 1 1 0 0 1 1 1 x x x x x",
|
||||
"x x x x x x x x 1 1 i i i i i i";
|
||||
min_write_delay = 9000;
|
||||
max_write_delay = 9000;
|
||||
;
|
||||
|
||||
memory "calibration"
|
||||
size = 1;
|
||||
read = "0 0 1 1 1 0 0 0 x x x x x x x x",
|
||||
"0 0 0 0 0 0 0 0 o o o o o o o o";
|
||||
;
|
||||
|
||||
memory "signature"
|
||||
size = 3;
|
||||
read = "0 0 1 1 0 0 0 0 x x x x x x x x",
|
||||
"x x x x x x a1 a0 o o o o o o o o";
|
||||
;
|
||||
;
|
||||
|
||||
|
1188
src/avrdude/avrdude-slic3r.conf.h
Normal file
@ -42,7 +42,6 @@ static void avrdude_oom_handler(const char *context, void *user_p)
|
||||
|
||||
struct AvrDude::priv
|
||||
{
|
||||
std::string sys_config;
|
||||
std::deque<std::vector<std::string>> args;
|
||||
bool cancelled = false;
|
||||
int exit_code = 0;
|
||||
@ -54,8 +53,6 @@ struct AvrDude::priv
|
||||
|
||||
std::thread avrdude_thread;
|
||||
|
||||
priv(std::string &&sys_config) : sys_config(sys_config) {}
|
||||
|
||||
void set_handlers();
|
||||
void unset_handlers();
|
||||
int run_one(const std::vector<std::string> &args);
|
||||
@ -96,14 +93,21 @@ 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);
|
||||
|
||||
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data(), sys_config.c_str());
|
||||
message_fn(command_line.c_str(), command_line.size());
|
||||
|
||||
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data());
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -123,7 +127,7 @@ int AvrDude::priv::run() {
|
||||
|
||||
// Public
|
||||
|
||||
AvrDude::AvrDude(std::string sys_config) : p(new priv(std::move(sys_config))) {}
|
||||
AvrDude::AvrDude() : p(new priv()) {}
|
||||
|
||||
AvrDude::AvrDude(AvrDude &&other) : p(std::move(other.p)) {}
|
||||
|
||||
|
@ -22,8 +22,7 @@ public:
|
||||
typedef std::function<void(const char * /* task */, unsigned /* progress */)> ProgressFn;
|
||||
typedef std::function<void()> CompleteFn;
|
||||
|
||||
// Main c-tor, sys_config is the location of avrdude's main configuration file
|
||||
AvrDude(std::string sys_config);
|
||||
AvrDude();
|
||||
AvrDude(AvrDude &&);
|
||||
AvrDude(const AvrDude &) = delete;
|
||||
AvrDude &operator=(AvrDude &&) = delete;
|
||||
|
@ -55,7 +55,7 @@ void avrdude_cancel();
|
||||
#define MSG_TRACE (4) /* displayed with -vvvv, show trace commuication */
|
||||
#define MSG_TRACE2 (5) /* displayed with -vvvvv */
|
||||
|
||||
int avrdude_main(int argc, char * argv [], const char *sys_config);
|
||||
int avrdude_main(int argc, char * argv []);
|
||||
|
||||
#if defined(WIN32NATIVE)
|
||||
|
||||
|
41
src/avrdude/conf-generate.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <ios>
|
||||
#include <iomanip>
|
||||
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
if (argc != 3) {
|
||||
std::cerr << "Usage: " << argv[0] << " <file> <symbol name>" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char* filename = argv[1];
|
||||
const char* symbol = argv[2];
|
||||
|
||||
size_t size = 0;
|
||||
std::fstream file(filename);
|
||||
if (!file.good()) {
|
||||
std::cerr << "Cannot read file: " << filename << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "/* WARN: This file is auto-generated from `" << filename << "` */" << std::endl;
|
||||
std::cout << "unsigned char " << symbol << "[] = {";
|
||||
|
||||
char c;
|
||||
std::cout << std::hex;
|
||||
std::cout.fill('0');
|
||||
for (file.get(c); !file.eof(); size++, file.get(c)) {
|
||||
if (size % 12 == 0) { std::cout << "\n "; }
|
||||
std::cout << "0x" << std::setw(2) << (unsigned)c << ", ";
|
||||
}
|
||||
|
||||
std::cout << "\n 0, 0\n};\n";
|
||||
|
||||
std::cout << std::dec;
|
||||
std::cout << "size_t " << symbol << "_size = " << size << ";" << std::endl;
|
||||
std::cout << "size_t " << symbol << "_size_yy = " << size + 2 << ";" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
@ -32,6 +32,8 @@
|
||||
|
||||
#include "config_gram.h"
|
||||
|
||||
#include "avrdude-slic3r.conf.h" // Embedded config file
|
||||
|
||||
char default_programmer[MAX_STR_CONST];
|
||||
char default_parallel[PATH_MAX];
|
||||
char default_serial[PATH_MAX];
|
||||
@ -325,7 +327,7 @@ int read_config(const char * file)
|
||||
FILE * f;
|
||||
int r;
|
||||
|
||||
f = fopen(file, "r");
|
||||
f = fopen_utf8(file, "r");
|
||||
if (f == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: can't open config file \"%s\": %s\n",
|
||||
progname, file, strerror(errno));
|
||||
@ -347,3 +349,33 @@ int read_config(const char * file)
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||
extern YY_BUFFER_STATE yy_scan_bytes(char *base, size_t size);
|
||||
extern void yy_delete_buffer(YY_BUFFER_STATE b);
|
||||
|
||||
int read_config_builtin()
|
||||
{
|
||||
int r;
|
||||
|
||||
lineno = 1;
|
||||
infile = "(builtin)";
|
||||
|
||||
// Note: Can't use yy_scan_buffer, it's buggy (?), leads to fread from a null FILE*
|
||||
// and so unfortunatelly we have to use the copying variant here
|
||||
YY_BUFFER_STATE buffer = yy_scan_bytes(avrdude_slic3r_conf, avrdude_slic3r_conf_size);
|
||||
if (buffer == NULL) {
|
||||
avrdude_message(MSG_INFO, "%s: read_config_builtin: Failed to initialize parsing buffer\n", progname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = yyparse();
|
||||
yy_delete_buffer(buffer);
|
||||
|
||||
#ifdef HAVE_YYLEX_DESTROY
|
||||
/* reset lexer and free any allocated memory */
|
||||
yylex_destroy();
|
||||
#endif
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -40,6 +40,9 @@
|
||||
#include "avrdude.h"
|
||||
#include "libavrdude.h"
|
||||
|
||||
#if defined(WIN32NATIVE)
|
||||
#include "windows/utf8.h"
|
||||
#endif
|
||||
|
||||
#define IHEX_MAXDATA 256
|
||||
|
||||
@ -102,21 +105,25 @@ static int fmt_autodetect(char * fname, unsigned section);
|
||||
|
||||
|
||||
|
||||
static FILE *fopen_and_seek(const char *filename, const char *mode, unsigned section)
|
||||
FILE *fopen_utf8(const char *filename, const char *mode)
|
||||
{
|
||||
FILE *file;
|
||||
// On Windows we need to convert the filename to UTF-16
|
||||
#if defined(WIN32NATIVE)
|
||||
static wchar_t fname_buffer[PATH_MAX];
|
||||
static wchar_t mode_buffer[MAX_MODE_LEN];
|
||||
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, fname_buffer, PATH_MAX) == 0) { return NULL; }
|
||||
if (MultiByteToWideChar(CP_ACP, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; }
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, mode, -1, mode_buffer, MAX_MODE_LEN) == 0) { return NULL; }
|
||||
|
||||
file = _wfopen(fname_buffer, mode_buffer);
|
||||
return _wfopen(fname_buffer, mode_buffer);
|
||||
#else
|
||||
file = fopen(filename, mode);
|
||||
return fopen(filename, mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
static FILE *fopen_and_seek(const char *filename, const char *mode, unsigned section)
|
||||
{
|
||||
FILE *file = fopen_utf8(filename, mode);
|
||||
|
||||
if (file == NULL) {
|
||||
return NULL;
|
||||
|
@ -1527,8 +1527,10 @@ static int jtagmkII_open(PROGRAMMER * pgm, char * port)
|
||||
*/
|
||||
jtagmkII_drain(pgm, 0);
|
||||
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_JTAG) < 0)
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_JTAG) < 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1579,8 +1581,10 @@ static int jtagmkII_open_dw(PROGRAMMER * pgm, char * port)
|
||||
*/
|
||||
jtagmkII_drain(pgm, 0);
|
||||
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_DEBUGWIRE) < 0)
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_DEBUGWIRE) < 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1631,8 +1635,10 @@ static int jtagmkII_open_pdi(PROGRAMMER * pgm, char * port)
|
||||
*/
|
||||
jtagmkII_drain(pgm, 0);
|
||||
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_PDI) < 0)
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_PDI) < 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1684,8 +1690,10 @@ static int jtagmkII_dragon_open(PROGRAMMER * pgm, char * port)
|
||||
*/
|
||||
jtagmkII_drain(pgm, 0);
|
||||
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_JTAG) < 0)
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_JTAG) < 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1737,8 +1745,10 @@ static int jtagmkII_dragon_open_dw(PROGRAMMER * pgm, char * port)
|
||||
*/
|
||||
jtagmkII_drain(pgm, 0);
|
||||
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_DEBUGWIRE) < 0)
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_DEBUGWIRE) < 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1790,8 +1800,10 @@ static int jtagmkII_dragon_open_pdi(PROGRAMMER * pgm, char * port)
|
||||
*/
|
||||
jtagmkII_drain(pgm, 0);
|
||||
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_PDI) < 0)
|
||||
if (jtagmkII_getsync(pgm, EMULATOR_MODE_PDI) < 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3370,6 +3382,8 @@ static int jtagmkII_open32(PROGRAMMER * pgm, char * port)
|
||||
status = jtagmkII_getsync(pgm, -1);
|
||||
if(status < 0) return -1;
|
||||
|
||||
// FIXME: Error handling is bad here: memory leak in resp (?) and port not closed
|
||||
|
||||
// AVR32 "special"
|
||||
buf[0] = CMND_SET_PARAMETER;
|
||||
buf[1] = 0x2D;
|
||||
|
@ -820,6 +820,8 @@ extern "C" {
|
||||
|
||||
char * fmtstr(FILEFMT format);
|
||||
|
||||
FILE *fopen_utf8(const char *filename, const char *mode);
|
||||
|
||||
int fileio(int op, char * filename, FILEFMT format,
|
||||
struct avrpart * p, char * memtype, int size, unsigned section);
|
||||
|
||||
@ -939,6 +941,7 @@ int init_config(void);
|
||||
void cleanup_config(void);
|
||||
|
||||
int read_config(const char * file);
|
||||
int read_config_builtin();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
#include "avrdude.h"
|
||||
|
||||
|
||||
static const char* SYS_CONFIG = "/etc/avrdude-slic3r.conf";
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return avrdude_main(argc, argv, SYS_CONFIG);
|
||||
}
|
54
src/avrdude/main-standalone.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
extern "C" {
|
||||
#include "avrdude.h"
|
||||
}
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
#include <stdlib.h>
|
||||
#include <vector>
|
||||
|
||||
extern "C" {
|
||||
#include "windows/utf8.h"
|
||||
}
|
||||
|
||||
struct ArgvUtf8 : std::vector<char*>
|
||||
{
|
||||
int argc;
|
||||
|
||||
ArgvUtf8(int argc_w, wchar_t *argv_w[]) : std::vector<char*>(argc_w + 1, nullptr), argc(0)
|
||||
{
|
||||
for (int i = 0; i < argc_w; i++) {
|
||||
char *arg_utf8 = ::wstr_to_utf8(argv_w[i], -1);
|
||||
if (arg_utf8 != nullptr) {
|
||||
operator[](i) = arg_utf8;
|
||||
argc = i + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~ArgvUtf8()
|
||||
{
|
||||
for (char *arg : *this) {
|
||||
if (arg != nullptr) {
|
||||
::free(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int wmain(int argc_w, wchar_t *argv_w[])
|
||||
{
|
||||
ArgvUtf8 argv_utf8(argc_w, argv_w);
|
||||
return ::avrdude_main(argv_utf8.argc, &argv_utf8[0]);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return ::avrdude_main(argc, argv);
|
||||
}
|
||||
|
||||
#endif
|
@ -426,7 +426,7 @@ static int cleanup_main(int status)
|
||||
/*
|
||||
* main routine
|
||||
*/
|
||||
int avrdude_main(int argc, char * argv [], const char *sys_config)
|
||||
int avrdude_main(int argc, char * argv [])
|
||||
{
|
||||
int rc; /* general return code checking */
|
||||
int exitrc; /* exit code for main() */
|
||||
@ -807,13 +807,15 @@ int avrdude_main(int argc, char * argv [], const char *sys_config)
|
||||
"%sCopyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/\n"
|
||||
"%sCopyright (c) 2007-2014 Joerg Wunsch\n\n",
|
||||
progname, version, __DATE__, __TIME__, progbuf, progbuf);
|
||||
avrdude_message(MSG_NOTICE, "%sSystem wide configuration file is \"%s\"\n",
|
||||
progbuf, sys_config);
|
||||
// avrdude_message(MSG_NOTICE, "%sSystem wide configuration file is \"%s\"\n",
|
||||
// progbuf, sys_config);
|
||||
|
||||
rc = read_config(sys_config);
|
||||
// rc = read_config(sys_config);
|
||||
rc = read_config_builtin();
|
||||
if (rc) {
|
||||
avrdude_message(MSG_INFO, "%s: error reading system wide configuration file \"%s\"\n",
|
||||
progname, sys_config);
|
||||
// avrdude_message(MSG_INFO, "%s: error reading system wide configuration file \"%s\"\n",
|
||||
// progname, sys_config);
|
||||
avrdude_message(MSG_INFO, "%s: error reading built-in configuration file\n", progname);
|
||||
return cleanup_main(1);
|
||||
}
|
||||
|
||||
@ -1082,6 +1084,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;
|
||||
|
@ -44,7 +44,7 @@
|
||||
#include "avrdude.h"
|
||||
#include "libavrdude.h"
|
||||
|
||||
long serial_recv_timeout = 5000; /* ms */
|
||||
long serial_recv_timeout = 4000; /* ms */
|
||||
#define MAX_ZERO_READS 512
|
||||
|
||||
struct baud_mapping {
|
||||
@ -150,6 +150,68 @@ static int ser_setspeed(union filedescriptor *fd, long baud)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Timeout read & write variants
|
||||
// Additionally to the regular -1 on I/O error, they return -2 on timeout
|
||||
ssize_t read_timeout(int fd, void *buf, size_t count, long timeout)
|
||||
{
|
||||
struct timeval tm, tm2;
|
||||
fd_set rfds;
|
||||
int nfds;
|
||||
|
||||
tm.tv_sec = timeout / 1000L;
|
||||
tm.tv_usec = (timeout % 1000L) * 1000;
|
||||
|
||||
while (1) {
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
tm2 = tm;
|
||||
nfds = select(fd + 1, &rfds, NULL, NULL, &tm2);
|
||||
|
||||
if (nfds == 0) {
|
||||
return -2;
|
||||
} else if (nfds == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
continue;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return read(fd, buf, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t write_timeout(int fd, const void *buf, size_t count, long timeout)
|
||||
{
|
||||
struct timeval tm, tm2;
|
||||
fd_set wfds;
|
||||
int nfds;
|
||||
|
||||
tm.tv_sec = timeout / 1000L;
|
||||
tm.tv_usec = (timeout % 1000L) * 1000;
|
||||
|
||||
while (1) {
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(fd, &wfds);
|
||||
tm2 = tm;
|
||||
nfds = select(fd + 1, NULL, &wfds, NULL, &tm2);
|
||||
|
||||
if (nfds == 0) {
|
||||
return -2;
|
||||
} else if (nfds == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
continue;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
return write(fd, buf, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Given a port description of the form <host>:<port>, open a TCP
|
||||
* connection to the specified destination, which is assumed to be a
|
||||
@ -314,6 +376,7 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
|
||||
int rc;
|
||||
const unsigned char * p = buf;
|
||||
size_t len = buflen;
|
||||
unsigned zero_writes = 0;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
@ -341,14 +404,25 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
|
||||
|
||||
while (len) {
|
||||
RETURN_IF_CANCEL();
|
||||
rc = write(fd->ifd, p, (len > 1024) ? 1024 : len);
|
||||
if (rc < 0) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_send(): write error: %s\n",
|
||||
progname, strerror(errno));
|
||||
rc = write_timeout(fd->ifd, p, (len > 1024) ? 1024 : len, serial_recv_timeout);
|
||||
if (rc == -2) {
|
||||
avrdude_message(MSG_NOTICE2, "%s: ser_send(): programmer is not responding\n", progname);
|
||||
return -1;
|
||||
} else if (rc == -1) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_send(): write error: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
} else if (rc == 0) {
|
||||
zero_writes++;
|
||||
if (zero_writes > MAX_ZERO_READS) {
|
||||
avrdude_message(MSG_NOTICE2, "%s: ser_send(): programmer is not responding (too many zero writes)\n",
|
||||
progname);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
zero_writes = 0;
|
||||
p += rc;
|
||||
len -= rc;
|
||||
}
|
||||
p += rc;
|
||||
len -= rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -357,51 +431,21 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
|
||||
|
||||
static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen)
|
||||
{
|
||||
struct timeval timeout, to2;
|
||||
fd_set rfds;
|
||||
int nfds;
|
||||
int rc;
|
||||
unsigned char * p = buf;
|
||||
size_t len = 0;
|
||||
unsigned zero_reads = 0;
|
||||
|
||||
timeout.tv_sec = serial_recv_timeout / 1000L;
|
||||
timeout.tv_usec = (serial_recv_timeout % 1000L) * 1000;
|
||||
to2 = timeout;
|
||||
|
||||
while (len < buflen) {
|
||||
reselect:
|
||||
RETURN_IF_CANCEL();
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd->ifd, &rfds);
|
||||
|
||||
nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &to2);
|
||||
// FIXME: The timeout has different behaviour on Linux vs other Unices
|
||||
// On Linux, the timeout is modified by subtracting the time spent,
|
||||
// on OS X (for example), it is not modified.
|
||||
// POSIX recommends re-initializing it before selecting.
|
||||
if (nfds == 0) {
|
||||
avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n",
|
||||
progname);
|
||||
rc = read_timeout(fd->ifd, p, (buflen - len > 1024) ? 1024 : buflen - len, serial_recv_timeout);
|
||||
|
||||
if (rc == -2) {
|
||||
avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n", progname);
|
||||
return -1;
|
||||
}
|
||||
else if (nfds == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_recv(): programmer is not responding,reselecting\n",
|
||||
progname);
|
||||
goto reselect;
|
||||
}
|
||||
else {
|
||||
avrdude_message(MSG_INFO, "%s: ser_recv(): select(): %s\n",
|
||||
progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
rc = read(fd->ifd, p, (buflen - len > 1024) ? 1024 : buflen - len);
|
||||
if (rc < 0) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n",
|
||||
progname, strerror(errno));
|
||||
} else if (rc == -1) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
} else if (rc == 0) {
|
||||
zero_reads++;
|
||||
@ -445,49 +489,26 @@ static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen
|
||||
|
||||
static int ser_drain(union filedescriptor *fd, int display)
|
||||
{
|
||||
struct timeval timeout;
|
||||
fd_set rfds;
|
||||
int nfds;
|
||||
int rc;
|
||||
unsigned char buf;
|
||||
unsigned zero_reads = 0;
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 250000;
|
||||
|
||||
if (display) {
|
||||
avrdude_message(MSG_INFO, "drain>");
|
||||
}
|
||||
|
||||
while (1) {
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd->ifd, &rfds);
|
||||
|
||||
reselect:
|
||||
RETURN_IF_CANCEL();
|
||||
nfds = select(fd->ifd + 1, &rfds, NULL, NULL, &timeout);
|
||||
if (nfds == 0) {
|
||||
|
||||
rc = read_timeout(fd->ifd, &buf, 1, 250); // Note: timeout needs to be kept low to not timeout in programmers
|
||||
if (rc == -2) {
|
||||
if (display) {
|
||||
avrdude_message(MSG_INFO, "<drain\n");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else if (nfds == -1) {
|
||||
if (errno == EINTR) {
|
||||
goto reselect;
|
||||
}
|
||||
else {
|
||||
avrdude_message(MSG_INFO, "%s: ser_drain(): select(): %s\n",
|
||||
progname, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
rc = read(fd->ifd, &buf, 1);
|
||||
if (rc < 0) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_drain(): read error: %s\n",
|
||||
progname, strerror(errno));
|
||||
break;
|
||||
} else if (rc == -1) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_drain(): read error: %s\n", progname, strerror(errno));
|
||||
return -1;
|
||||
} else if (rc == 0) {
|
||||
zero_reads++;
|
||||
|
@ -34,16 +34,63 @@
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h> /* for isprint */
|
||||
#include <errno.h> /* ENOTTY */
|
||||
|
||||
#include "avrdude.h"
|
||||
#include "libavrdude.h"
|
||||
#include "windows/utf8.h"
|
||||
|
||||
long serial_recv_timeout = 5000; /* ms */
|
||||
|
||||
#define W32SERBUFSIZE 1024
|
||||
|
||||
|
||||
// Get last error message string in UTF-8
|
||||
// Always return a valid null-terminated string
|
||||
// The returned string should be freed by the caller
|
||||
char* last_error_string(int wsa)
|
||||
{
|
||||
LPWSTR wbuffer = NULL;
|
||||
|
||||
(void)wsa;
|
||||
|
||||
DWORD wbuffer_len = FormatMessageW(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
#ifdef HAVE_LIBWS2_32
|
||||
wsa ? WSAGetLastError() : GetLastError(),
|
||||
#else
|
||||
GetLastError(),
|
||||
#endif
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPWSTR)&wbuffer,
|
||||
0,
|
||||
NULL);
|
||||
|
||||
if (wbuffer_len == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *res = wstr_to_utf8(wbuffer, wbuffer_len);
|
||||
|
||||
LocalFree(wbuffer);
|
||||
|
||||
if (res == NULL) {
|
||||
// If we get here, conversion to UTF-8 failed
|
||||
res = strdup("(could not get error message)");
|
||||
if (res == NULL) {
|
||||
avrdude_oom("last_error_string(): out of memory\n");
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
struct baud_mapping {
|
||||
long baud;
|
||||
DWORD speed;
|
||||
@ -95,6 +142,7 @@ static BOOL serial_w32SetTimeOut(HANDLE hComPort, DWORD timeout) // in ms
|
||||
// ctmo.ReadIntervalTimeout = timeout;
|
||||
// ctmo.ReadTotalTimeoutMultiplier = timeout;
|
||||
ctmo.ReadTotalTimeoutConstant = timeout;
|
||||
ctmo.WriteTotalTimeoutConstant = timeout;
|
||||
|
||||
return SetCommTimeouts(hComPort, &ctmo);
|
||||
}
|
||||
@ -129,7 +177,6 @@ static int
|
||||
net_open(const char *port, union filedescriptor *fdp)
|
||||
{
|
||||
WSADATA wsaData;
|
||||
LPVOID lpMsgBuf;
|
||||
|
||||
char *hstr, *pstr, *end;
|
||||
unsigned int pnum;
|
||||
@ -175,18 +222,10 @@ net_open(const char *port, union filedescriptor *fdp)
|
||||
free(hstr);
|
||||
|
||||
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
WSAGetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&lpMsgBuf,
|
||||
0,
|
||||
NULL);
|
||||
avrdude_message(MSG_INFO, "%s: net_open(): Cannot open socket: %s\n", progname, (char *)lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
const char *error = last_error_string(1);
|
||||
avrdude_message(MSG_INFO, "%s: net_open(): Cannot open socket: %s\n", progname, error);
|
||||
free(error);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -196,18 +235,9 @@ net_open(const char *port, union filedescriptor *fdp)
|
||||
memcpy(&(sockaddr.sin_addr.s_addr), hp->h_addr, sizeof(struct in_addr));
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
WSAGetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&lpMsgBuf,
|
||||
0,
|
||||
NULL);
|
||||
avrdude_message(MSG_INFO, "%s: net_open(): Connect failed: %s\n", progname, (char *)lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
const char *error = last_error_string(1);
|
||||
avrdude_message(MSG_INFO, "%s: net_open(): Connect failed: %s\n", progname);
|
||||
free(error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -221,7 +251,6 @@ net_open(const char *port, union filedescriptor *fdp)
|
||||
|
||||
static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
|
||||
{
|
||||
LPVOID lpMsgBuf;
|
||||
HANDLE hComPort=INVALID_HANDLE_VALUE;
|
||||
char *newname = 0;
|
||||
|
||||
@ -261,19 +290,9 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (hComPort == INVALID_HANDLE_VALUE) {
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||||
(LPTSTR) &lpMsgBuf,
|
||||
0,
|
||||
NULL);
|
||||
avrdude_message(MSG_INFO, "%s: ser_open(): can't open device \"%s\": %s\n",
|
||||
progname, port, (char*)lpMsgBuf);
|
||||
LocalFree( lpMsgBuf );
|
||||
const char *error = last_error_string(0);
|
||||
avrdude_message(MSG_INFO, "%s: ser_open(): can't open device \"%s\": %s\n", progname, port, error);
|
||||
free(error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -346,14 +365,13 @@ static int ser_set_dtr_rts(union filedescriptor *fd, int is_on)
|
||||
#ifdef HAVE_LIBWS2_32
|
||||
static int net_send(union filedescriptor *fd, const unsigned char * buf, size_t buflen)
|
||||
{
|
||||
LPVOID lpMsgBuf;
|
||||
int rc;
|
||||
const unsigned char *p = buf;
|
||||
size_t len = buflen;
|
||||
|
||||
if (fd->ifd < 0) {
|
||||
avrdude_message(MSG_NOTICE, "%s: net_send(): connection not open\n", progname);
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!len) {
|
||||
@ -382,19 +400,10 @@ static int net_send(union filedescriptor *fd, const unsigned char * buf, size_t
|
||||
while (len) {
|
||||
rc = send(fd->ifd, p, (len > 1024) ? 1024 : len, 0);
|
||||
if (rc < 0) {
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
WSAGetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&lpMsgBuf,
|
||||
0,
|
||||
NULL);
|
||||
avrdude_message(MSG_INFO, "%s: net_send(): send error: %s\n", progname, (char *)lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
exit(1);
|
||||
const char *error = last_error_string(1);
|
||||
avrdude_message(MSG_INFO, "%s: net_send(): send error: %s\n", progname, error);
|
||||
free(error);
|
||||
return -1;
|
||||
}
|
||||
p += rc;
|
||||
len -= rc;
|
||||
@ -423,8 +432,7 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
|
||||
HANDLE hComPort=(HANDLE)fd->pfd;
|
||||
|
||||
if (hComPort == INVALID_HANDLE_VALUE) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_send(): port not open\n",
|
||||
progname);
|
||||
avrdude_message(MSG_INFO, "%s: ser_send(): port not open\n", progname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -449,18 +457,18 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
|
||||
}
|
||||
avrdude_message(MSG_INFO, "\n");
|
||||
}
|
||||
|
||||
|
||||
serial_w32SetTimeOut(hComPort,500);
|
||||
|
||||
if (!WriteFile (hComPort, buf, buflen, &written, NULL)) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_send(): write error: %s\n",
|
||||
progname, "sorry no info avail"); // TODO
|
||||
if (!WriteFile(hComPort, buf, buflen, &written, NULL)) {
|
||||
const char *error = last_error_string(0);
|
||||
avrdude_message(MSG_INFO, "%s: ser_send(): write error: %s\n", progname, error);
|
||||
free(error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (written != buflen) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_send(): size/send mismatch\n",
|
||||
progname);
|
||||
avrdude_message(MSG_INFO, "%s: ser_send(): size/send mismatch\n", progname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -471,7 +479,6 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
|
||||
#ifdef HAVE_LIBWS2_32
|
||||
static int net_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen)
|
||||
{
|
||||
LPVOID lpMsgBuf;
|
||||
struct timeval timeout, to2;
|
||||
fd_set rfds;
|
||||
int nfds;
|
||||
@ -481,7 +488,7 @@ static int net_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen
|
||||
|
||||
if (fd->ifd < 0) {
|
||||
avrdude_message(MSG_INFO, "%s: net_recv(): connection not open\n", progname);
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
timeout.tv_sec = serial_recv_timeout / 1000L;
|
||||
@ -504,37 +511,19 @@ reselect:
|
||||
avrdude_message(MSG_NOTICE, "%s: ser_recv(): programmer is not responding, reselecting\n", progname);
|
||||
goto reselect;
|
||||
} else {
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
WSAGetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&lpMsgBuf,
|
||||
0,
|
||||
NULL);
|
||||
avrdude_message(MSG_INFO, "%s: ser_recv(): select(): %s\n", progname, (char *)lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
exit(1);
|
||||
const char *error = last_error_string(1);
|
||||
avrdude_message(MSG_INFO, "%s: ser_recv(): select(): %s\n", progname, error);
|
||||
free(error);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
rc = recv(fd->ifd, p, (buflen - len > 1024) ? 1024 : buflen - len, 0);
|
||||
if (rc < 0) {
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
WSAGetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&lpMsgBuf,
|
||||
0,
|
||||
NULL);
|
||||
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n", progname, (char *)lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
exit(1);
|
||||
const char *error = last_error_string(1);
|
||||
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n", progname, error);
|
||||
free(error);
|
||||
return -1;
|
||||
}
|
||||
p += rc;
|
||||
len += rc;
|
||||
@ -579,37 +568,24 @@ static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen
|
||||
RETURN_IF_CANCEL();
|
||||
|
||||
HANDLE hComPort=(HANDLE)fd->pfd;
|
||||
|
||||
|
||||
if (hComPort == INVALID_HANDLE_VALUE) {
|
||||
avrdude_message(MSG_INFO, "%s: ser_read(): port not open\n",
|
||||
progname);
|
||||
avrdude_message(MSG_INFO, "%s: ser_read(): port not open\n", progname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
serial_w32SetTimeOut(hComPort, serial_recv_timeout);
|
||||
|
||||
|
||||
if (!ReadFile(hComPort, buf, buflen, &read, NULL)) {
|
||||
LPVOID lpMsgBuf;
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||||
(LPTSTR) &lpMsgBuf,
|
||||
0,
|
||||
NULL );
|
||||
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n",
|
||||
progname, (char*)lpMsgBuf);
|
||||
LocalFree( lpMsgBuf );
|
||||
const char *error = last_error_string(0);
|
||||
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n", progname, error);
|
||||
free(error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* time out detected */
|
||||
if (read == 0) {
|
||||
avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n",
|
||||
progname);
|
||||
avrdude_message(MSG_NOTICE2, "%s: ser_recv(): programmer is not responding\n", progname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -664,20 +640,9 @@ static int ser_drain(union filedescriptor *fd, int display)
|
||||
|
||||
readres=ReadFile(hComPort, buf, 1, &read, NULL);
|
||||
if (!readres) {
|
||||
LPVOID lpMsgBuf;
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL,
|
||||
GetLastError(),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
||||
(LPTSTR) &lpMsgBuf,
|
||||
0,
|
||||
NULL );
|
||||
avrdude_message(MSG_INFO, "%s: ser_drain(): read error: %s\n",
|
||||
progname, (char*)lpMsgBuf);
|
||||
LocalFree( lpMsgBuf );
|
||||
const char *error = last_error_string(0);
|
||||
avrdude_message(MSG_INFO, "%s: ser_drain(): read error: %s\n", progname, error);
|
||||
free(error);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -669,11 +669,15 @@ static int stk500_open(PROGRAMMER * pgm, char * port)
|
||||
|
||||
// MIB510 init
|
||||
if (strcmp(ldata(lfirst(pgm->id)), "mib510") == 0 &&
|
||||
mib510_isp(pgm, 1) != 0)
|
||||
mib510_isp(pgm, 1) != 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stk500_getsync(pgm) < 0)
|
||||
if (stk500_getsync(pgm) < 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1695,8 +1695,10 @@ static int stk500v2_open(PROGRAMMER * pgm, char * port)
|
||||
stk500v2_drain(pgm, 0);
|
||||
|
||||
if (pgm->bitclock != 0.0) {
|
||||
if (pgm->set_sck_period(pgm, pgm->bitclock) != 0)
|
||||
if (pgm->set_sck_period(pgm, pgm->bitclock) != 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1753,8 +1755,10 @@ static int stk600_open(PROGRAMMER * pgm, char * port)
|
||||
stk500v2_drain(pgm, 0);
|
||||
|
||||
if (pgm->bitclock != 0.0) {
|
||||
if (pgm->set_sck_period(pgm, pgm->bitclock) != 0)
|
||||
if (pgm->set_sck_period(pgm, pgm->bitclock) != 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
45
src/avrdude/windows/utf8.c
Normal file
@ -0,0 +1,45 @@
|
||||
#include "utf8.h"
|
||||
|
||||
|
||||
char* wstr_to_utf8(LPWSTR wstr, int len)
|
||||
{
|
||||
char *res = NULL;
|
||||
|
||||
int res_size = WideCharToMultiByte(CP_UTF8, 0, wstr, len, NULL, 0, NULL, NULL);
|
||||
if (res_size > 0) {
|
||||
// Note: WideCharToMultiByte doesn't null-terminate if real (ie. > 0) buffer length is passed
|
||||
res = malloc(len != - 1 ? res_size + 1 : res_size);
|
||||
if (res == NULL) { return NULL; }
|
||||
|
||||
if (WideCharToMultiByte(CP_UTF8, 0, wstr, len, res, res_size, NULL, NULL) == res_size) {
|
||||
if (len != -1) { res[res_size] = '\0'; }
|
||||
} else {
|
||||
free(res);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
LPWSTR utf8_to_wstr(const char *str, int len)
|
||||
{
|
||||
LPWSTR res = NULL;
|
||||
|
||||
int res_size = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0);
|
||||
if (res_size > 0) {
|
||||
// Note: MultiByteToWideChar doesn't null-terminate if real (ie. > 0) buffer length is passed
|
||||
res = malloc(len != - 1 ? res_size + 1 : res_size);
|
||||
|
||||
if (res == NULL) { return NULL; }
|
||||
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, str, len, res, res_size) == res_size) {
|
||||
if (len != -1) { res[res_size] = L'\0'; }
|
||||
} else {
|
||||
free(res);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
10
src/avrdude/windows/utf8.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef SLIC3R_AVRDUDE_UTF8_H
|
||||
#define SLIC3R_AVRDUDE_UTF8_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
extern char* wstr_to_utf8(LPWSTR wstr, int len);
|
||||
extern LPWSTR utf8_to_wstr(const char *str, int len);
|
||||
|
||||
|
||||
#endif // SLIC3R_AVRDUDE_UTF8_H
|
@ -192,8 +192,10 @@ static int wiring_open(PROGRAMMER * pgm, char * port)
|
||||
/* drain any extraneous input */
|
||||
stk500v2_drain(pgm, 0);
|
||||
|
||||
if (stk500v2_getsync(pgm) < 0)
|
||||
if (stk500v2_getsync(pgm) < 0) {
|
||||
serial_close(&pgm->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -163,7 +163,7 @@ public:
|
||||
{
|
||||
dir_ = OptDir::MIN;
|
||||
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
||||
objectfunction, initvals, Bound<Args>()... );
|
||||
forward<Func>(objectfunction), initvals, Bound<Args>()... );
|
||||
}
|
||||
|
||||
template<class...Args, class Func>
|
||||
@ -171,7 +171,7 @@ public:
|
||||
{
|
||||
dir_ = OptDir::MIN;
|
||||
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
||||
objectfunction,
|
||||
forward<Func>(objectfunction),
|
||||
Input<Args...>(),
|
||||
Bound<Args>()... );
|
||||
}
|
||||
@ -184,7 +184,7 @@ public:
|
||||
{
|
||||
dir_ = OptDir::MAX;
|
||||
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
||||
objectfunction, initvals, bounds... );
|
||||
forward<Func>(objectfunction), initvals, bounds... );
|
||||
}
|
||||
|
||||
template<class Func, class...Args>
|
||||
@ -193,7 +193,7 @@ public:
|
||||
{
|
||||
dir_ = OptDir::MAX;
|
||||
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
||||
objectfunction, initvals, Bound<Args>()... );
|
||||
forward<Func>(objectfunction), initvals, Bound<Args>()... );
|
||||
}
|
||||
|
||||
template<class...Args, class Func>
|
||||
@ -201,7 +201,7 @@ public:
|
||||
{
|
||||
dir_ = OptDir::MAX;
|
||||
return static_cast<Subclass*>(this)->template optimize<Func, Args...>(
|
||||
objectfunction,
|
||||
forward<Func>(objectfunction),
|
||||
Input<Args...>(),
|
||||
Bound<Args>()... );
|
||||
}
|
||||
|
@ -103,8 +103,9 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
|
||||
if (surface.has_fill_solid() && (!surface.has_mod_bridge() || layerm.layer()->id() == 0) && !surface.has_mod_overBridge()) {
|
||||
group_attrib[i].is_solid = true;
|
||||
group_attrib[i].flow_width = (surface.has_pos_top()) ? top_solid_infill_flow.width : solid_infill_flow.width;
|
||||
group_attrib[i].pattern = surface.has_pos_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().solid_fill_pattern.value;
|
||||
group_attrib[i].pattern = surface.has_pos_bottom() ? layerm.region()->config().bottom_fill_pattern.value : layerm.region()->config().solid_fill_pattern.value;
|
||||
group_attrib[i].pattern = surface.has_pos_external() ?
|
||||
(surface.has_pos_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) :
|
||||
layerm.region()->config().solid_fill_pattern.value;
|
||||
}
|
||||
}
|
||||
// Loop through solid groups, find compatible groups and append them to this one.
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include "3mf.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
@ -323,7 +325,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 +778,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 +822,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;
|
||||
|
||||
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>());
|
||||
if (version == 0) {
|
||||
for (unsigned int i=0; i<object_data_points.size(); i+=3)
|
||||
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 +1508,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
|
||||
@ -1726,6 +1751,11 @@ namespace Slic3r {
|
||||
bool _3MF_Exporter::_add_model_file_to_archive(mz_zip_archive& archive, Model& model)
|
||||
{
|
||||
std::stringstream stream;
|
||||
// https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
|
||||
// Conversion of a floating-point value to text and back is exact as long as at least max_digits10 were used (9 for float, 17 for double).
|
||||
// It is guaranteed to produce the same floating-point value, even though the intermediate text representation is not exact.
|
||||
// The default value of std::stream precision is 6 digits only!
|
||||
stream << std::setprecision(std::numeric_limits<float>::max_digits10);
|
||||
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
|
||||
@ -1841,7 +1871,7 @@ namespace Slic3r {
|
||||
for (int i = 0; i < stl.stats.shared_vertices; ++i)
|
||||
{
|
||||
stream << " <" << VERTEX_TAG << " ";
|
||||
Vec3d v = matrix * stl.v_shared[i].cast<double>();
|
||||
Vec3f v = (matrix * stl.v_shared[i].cast<double>()).cast<float>();
|
||||
stream << "x=\"" << v(0) << "\" ";
|
||||
stream << "y=\"" << v(1) << "\" ";
|
||||
stream << "z=\"" << v(2) << "\" />\n";
|
||||
@ -1961,7 +1991,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 +2000,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 +2009,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;
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <limits>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
@ -583,7 +584,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 +592,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 +605,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]));
|
||||
}
|
||||
@ -856,6 +857,11 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
||||
return false;
|
||||
|
||||
std::stringstream stream;
|
||||
// https://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10
|
||||
// Conversion of a floating-point value to text and back is exact as long as at least max_digits10 were used (9 for float, 17 for double).
|
||||
// It is guaranteed to produce the same floating-point value, even though the intermediate text representation is not exact.
|
||||
// The default value of std::stream precision is 6 digits only!
|
||||
stream << std::setprecision(std::numeric_limits<float>::max_digits10);
|
||||
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
stream << "<amf unit=\"millimeter\">\n";
|
||||
stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n";
|
||||
@ -900,14 +906,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";
|
||||
}
|
||||
@ -927,7 +933,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
||||
for (size_t i = 0; i < stl.stats.shared_vertices; ++i) {
|
||||
stream << " <vertex>\n";
|
||||
stream << " <coordinates>\n";
|
||||
Vec3d v = matrix * stl.v_shared[i].cast<double>();
|
||||
Vec3f v = (matrix * stl.v_shared[i].cast<double>()).cast<float>();
|
||||
stream << " <x>" << v(0) << "</x>\n";
|
||||
stream << " <y>" << v(1) << "</y>\n";
|
||||
stream << " <z>" << v(2) << "</z>\n";
|
||||
|
@ -2636,8 +2636,14 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string
|
||||
m_last_extrusion_role = path.role();
|
||||
|
||||
// adds analyzer tags and updates analyzer's tracking data
|
||||
if (m_enable_analyzer) {
|
||||
if (path.role() != m_last_analyzer_extrusion_role) {
|
||||
if (m_enable_analyzer)
|
||||
{
|
||||
// PrusaMultiMaterial::Writer may generate GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines without updating m_last_height and m_last_width
|
||||
// so, if the last role was erWipeTower we force export of GCodeAnalyzer::Height_Tag and GCodeAnalyzer::Width_Tag lines
|
||||
bool last_was_wipe_tower = (m_last_analyzer_extrusion_role == erWipeTower);
|
||||
|
||||
if (path.role() != m_last_analyzer_extrusion_role)
|
||||
{
|
||||
m_last_analyzer_extrusion_role = path.role();
|
||||
char buf[32];
|
||||
sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), int(m_last_analyzer_extrusion_role));
|
||||
@ -2652,7 +2658,8 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string
|
||||
gcode += buf;
|
||||
}
|
||||
|
||||
if (m_last_width != path.width) {
|
||||
if (last_was_wipe_tower || (m_last_width != path.width))
|
||||
{
|
||||
m_last_width = path.width;
|
||||
|
||||
char buf[32];
|
||||
@ -2660,7 +2667,8 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string
|
||||
gcode += buf;
|
||||
}
|
||||
|
||||
if (m_last_height != path.height) {
|
||||
if (last_was_wipe_tower || (m_last_height != path.height))
|
||||
{
|
||||
m_last_height = path.height;
|
||||
|
||||
char buf[32];
|
||||
|
@ -482,6 +482,7 @@ public:
|
||||
bool
|
||||
arrange(size_t total_parts, const Vec2d &part_size, coordf_t dist, const BoundingBoxf* bb, Pointfs &positions)
|
||||
{
|
||||
std::cout << "calling geometry::arrange()\n";
|
||||
positions.clear();
|
||||
|
||||
Vec2d part = part_size;
|
||||
|
@ -212,6 +212,7 @@ public:
|
||||
|
||||
void set_scaling_factor(const Vec3d& scaling_factor);
|
||||
void set_scaling_factor(Axis axis, double scaling_factor);
|
||||
bool is_scaling_uniform() const { return std::abs(m_scaling_factor.x() - m_scaling_factor.y()) < 1e-8 && std::abs(m_scaling_factor.x() - m_scaling_factor.z()) < 1e-8; }
|
||||
|
||||
const Vec3d& get_mirror() const { return m_mirror; }
|
||||
double get_mirror(Axis axis) const { return m_mirror(axis); }
|
||||
|
@ -592,6 +592,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
|
||||
this->input_file = rhs.input_file;
|
||||
this->config = rhs.config;
|
||||
this->sla_support_points = rhs.sla_support_points;
|
||||
this->sla_points_status = rhs.sla_points_status;
|
||||
this->layer_height_ranges = rhs.layer_height_ranges;
|
||||
this->layer_height_profile = rhs.layer_height_profile;
|
||||
this->origin_translation = rhs.origin_translation;
|
||||
@ -625,6 +626,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
|
||||
this->input_file = std::move(rhs.input_file);
|
||||
this->config = std::move(rhs.config);
|
||||
this->sla_support_points = std::move(rhs.sla_support_points);
|
||||
this->sla_points_status = std::move(rhs.sla_points_status);
|
||||
this->layer_height_ranges = std::move(rhs.layer_height_ranges);
|
||||
this->layer_height_profile = std::move(rhs.layer_height_profile);
|
||||
this->origin_translation = std::move(rhs.origin_translation);
|
||||
@ -1130,6 +1132,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
||||
if (keep_upper) {
|
||||
upper->set_model(nullptr);
|
||||
upper->sla_support_points.clear();
|
||||
upper->sla_points_status = sla::PointsStatus::None;
|
||||
upper->clear_volumes();
|
||||
upper->input_file = "";
|
||||
}
|
||||
@ -1137,6 +1140,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
||||
if (keep_lower) {
|
||||
lower->set_model(nullptr);
|
||||
lower->sla_support_points.clear();
|
||||
lower->sla_points_status = sla::PointsStatus::None;
|
||||
lower->clear_volumes();
|
||||
lower->input_file = "";
|
||||
}
|
||||
@ -1480,32 +1484,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 +1675,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,11 @@ 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;
|
||||
// To keep track of where the points came from (used for synchronization between
|
||||
// the SLA gizmo and the backend).
|
||||
sla::PointsStatus sla_points_status = sla::PointsStatus::None;
|
||||
|
||||
/* 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 +298,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 +319,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 +361,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 +407,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 +419,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 +637,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;
|
||||
|
@ -286,7 +286,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;
|
||||
}
|
||||
@ -555,10 +555,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())
|
||||
model_volume_dst->set_transformation(model_volume_src->get_transformation());
|
||||
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.
|
||||
@ -573,7 +577,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();) {
|
||||
@ -719,7 +723,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);
|
||||
@ -751,7 +755,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));
|
||||
@ -843,10 +847,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 ||
|
||||
@ -861,7 +865,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()));
|
||||
@ -888,8 +892,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;
|
||||
@ -962,7 +966,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.
|
||||
@ -1513,7 +1517,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;
|
||||
}
|
||||
@ -2035,7 +2040,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)
|
||||
@ -2075,7 +2080,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) {}
|
||||
@ -244,8 +269,9 @@ public:
|
||||
// Bitmap of flags.
|
||||
enum FlagBits {
|
||||
DEFAULT,
|
||||
NO_RELOAD_SCENE = 0,
|
||||
RELOAD_SCENE = 1,
|
||||
NO_RELOAD_SCENE = 0,
|
||||
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.
|
||||
|
@ -59,6 +59,17 @@ void PrintConfigDef::init_common_params()
|
||||
def->cli = "max-print-height=f";
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(200.0);
|
||||
|
||||
def = this->add("slice_closing_radius", coFloat);
|
||||
def->label = L("Slice gap closing radius");
|
||||
def->category = L("Advanced");
|
||||
def->tooltip = L("Cracks smaller than 2x gap closing radius are being filled during the triangle mesh slicing. "
|
||||
"The gap closing operation may reduce the final print resolution, therefore it is advisable to keep the value reasonably low.");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "slice-closing-radius=f";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(0.049);
|
||||
}
|
||||
|
||||
void PrintConfigDef::init_fff_params()
|
||||
@ -78,15 +89,6 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("remove_small_gaps", coBool);
|
||||
def->label = L("Remove small gaps");
|
||||
def->tooltip = L("Remove the small gaps in the 3D model when slicing. Disable it if you "
|
||||
"are very confident on your model, or you want to print an item with a geometry "
|
||||
"designed for vase mode.");
|
||||
def->cli = "remove-small-gaps!";
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("bed_temperature", coInts);
|
||||
def->label = L("Other layers");
|
||||
def->full_label = L("Bed temperature");
|
||||
@ -417,7 +419,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("top_fill_pattern", coEnum);
|
||||
def->label = L("Top Pattern");
|
||||
def->category = L("Infill");
|
||||
def->tooltip = L("Fill pattern for top infill. This only affects the top external visible layer, and not its adjacent solid shells.");
|
||||
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=s";
|
||||
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
def->enum_values.push_back("rectilinear");
|
||||
@ -443,7 +445,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("bottom_fill_pattern", coEnum);
|
||||
def->label = L("Bottom Pattern");
|
||||
def->category = L("Infill");
|
||||
def->tooltip = L("Fill pattern for bottom infill. This only affects the bottom external visible layer, and not its adjacent solid shells.");
|
||||
def->tooltip = L("Fill pattern for bottom infill. This only affects the bottom visible layer, and not its adjacent solid shells.");
|
||||
def->cli = "bottom-fill-pattern|external-fill-pattern=s";
|
||||
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
def->enum_values.push_back("rectilinear");
|
||||
@ -2972,6 +2974,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");
|
||||
@ -2987,6 +3015,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");
|
||||
@ -3167,28 +3203,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;
|
||||
@ -3236,6 +3263,17 @@ void PrintConfigDef::init_sla_params()
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloat(1.0);
|
||||
|
||||
def = this->add("pad_wall_slope", coFloat);
|
||||
def->label = L("Pad wall slope");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("The slope of the pad wall relative to the bed plane. "
|
||||
"90 degrees means straight walls.");
|
||||
def->sidetext = L("degrees");
|
||||
def->cli = "";
|
||||
def->min = 45;
|
||||
def->max = 90;
|
||||
def->default_value = new ConfigOptionFloat(45.0);
|
||||
}
|
||||
|
||||
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
|
||||
@ -3395,13 +3433,38 @@ double PrintConfig::min_object_distance() const
|
||||
|
||||
double PrintConfig::min_object_distance(const ConfigBase *config)
|
||||
{
|
||||
double extruder_clearance_radius = config->option("extruder_clearance_radius")->getFloat();
|
||||
double duplicate_distance = config->option("duplicate_distance")->getFloat();
|
||||
|
||||
// min object distance is max(duplicate_distance, clearance_radius)
|
||||
return (config->option("complete_objects")->getBool() && extruder_clearance_radius > duplicate_distance)
|
||||
? extruder_clearance_radius
|
||||
: duplicate_distance;
|
||||
double base_dist = config->option("duplicate_distance")->getFloat();
|
||||
if (config->option("complete_objects")->getBool()){
|
||||
std::cout << "min distance: complete objects\n";
|
||||
std::vector<double> vals = dynamic_cast<const ConfigOptionFloats*>(config->option("nozzle_diameter"))->values;
|
||||
double max_nozzle_diam = 0;
|
||||
for (double val : vals) max_nozzle_diam = std::fmax(max_nozzle_diam, val);
|
||||
|
||||
// min object distance is max(duplicate_distance, clearance_radius)
|
||||
double extruder_clearance_radius = config->option("extruder_clearance_radius")->getFloat();
|
||||
if (extruder_clearance_radius > base_dist){
|
||||
std::cout << "min distance: set for extruder_clearance_radius: " << base_dist << " + " << extruder_clearance_radius << " + " << max_nozzle_diam << "\n";
|
||||
base_dist = extruder_clearance_radius + max_nozzle_diam;
|
||||
}
|
||||
//add brim width
|
||||
if (config->option("brim_width")->getFloat() > 0){
|
||||
std::cout << "min distance: add for brim_width: " << base_dist << " += " << (config->option("brim_width")->getFloat()) << "\n";
|
||||
base_dist += config->option("brim_width")->getFloat();
|
||||
}
|
||||
//add the skirt
|
||||
if (config->option("skirts")->getInt() > 0){
|
||||
//add skirt dist
|
||||
std::cout << "min distance: set for skirt_distance: " << base_dist << " += " << config->option("skirt_distance")->getFloat() << "\n";
|
||||
double dist_skirt = config->option("skirt_distance")->getFloat();
|
||||
if (dist_skirt > config->option("brim_width")->getFloat())
|
||||
base_dist += dist_skirt - config->option("brim_width")->getFloat();
|
||||
//add skirt width
|
||||
std::cout << "min distance: add for skirts: " << base_dist << " += " << (max_nozzle_diam*config->option("skirts")->getInt()*1.5) << "\n";
|
||||
base_dist += max_nozzle_diam * config->option("skirts")->getInt() * 1.5;
|
||||
}
|
||||
}
|
||||
std::cout << "min distance: final=" << base_dist << "\n";
|
||||
return base_dist;
|
||||
}
|
||||
|
||||
std::string FullPrintConfig::validate()
|
||||
|
@ -451,7 +451,6 @@ class PrintObjectConfig : public StaticPrintConfig
|
||||
STATIC_PRINT_CONFIG_CACHE(PrintObjectConfig)
|
||||
public:
|
||||
ConfigOptionBool clip_multipart_objects;
|
||||
ConfigOptionBool remove_small_gaps;
|
||||
ConfigOptionBool dont_support_bridges;
|
||||
ConfigOptionFloat elefant_foot_compensation;
|
||||
ConfigOptionFloatOrPercent extrusion_width;
|
||||
@ -466,6 +465,7 @@ public:
|
||||
ConfigOptionBool seam_travel;
|
||||
// ConfigOptionFloat seam_preferred_direction;
|
||||
// ConfigOptionFloat seam_preferred_direction_jitter;
|
||||
ConfigOptionFloat slice_closing_radius;
|
||||
ConfigOptionBool support_material;
|
||||
// Automatic supports (generated based on support_material_threshold).
|
||||
ConfigOptionBool support_material_auto;
|
||||
@ -503,7 +503,6 @@ protected:
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
OPT_PTR(clip_multipart_objects);
|
||||
OPT_PTR(remove_small_gaps);
|
||||
OPT_PTR(dont_support_bridges);
|
||||
OPT_PTR(elefant_foot_compensation);
|
||||
OPT_PTR(extrusion_width);
|
||||
@ -515,6 +514,7 @@ protected:
|
||||
OPT_PTR(raft_layers);
|
||||
OPT_PTR(seam_position);
|
||||
OPT_PTR(seam_travel);
|
||||
OPT_PTR(slice_closing_radius);
|
||||
// OPT_PTR(seam_preferred_direction);
|
||||
// OPT_PTR(seam_preferred_direction_jitter);
|
||||
OPT_PTR(support_material);
|
||||
@ -557,11 +557,11 @@ public:
|
||||
ConfigOptionFloat bridge_flow_ratio;
|
||||
ConfigOptionFloat over_bridge_flow_ratio;
|
||||
ConfigOptionEnum<InfillPattern> bottom_fill_pattern;
|
||||
ConfigOptionFloatOrPercent bridged_infill_margin;
|
||||
ConfigOptionFloat bridge_speed;
|
||||
ConfigOptionBool ensure_vertical_shell_thickness;
|
||||
ConfigOptionBool enforce_full_fill_volume;
|
||||
ConfigOptionFloatOrPercent external_infill_margin;
|
||||
ConfigOptionFloatOrPercent bridged_infill_margin;
|
||||
ConfigOptionFloatOrPercent external_perimeter_extrusion_width;
|
||||
ConfigOptionFloatOrPercent external_perimeter_speed;
|
||||
ConfigOptionBool external_perimeters_first;
|
||||
@ -616,11 +616,11 @@ protected:
|
||||
OPT_PTR(bridge_flow_ratio);
|
||||
OPT_PTR(over_bridge_flow_ratio);
|
||||
OPT_PTR(bottom_fill_pattern);
|
||||
OPT_PTR(bridged_infill_margin);
|
||||
OPT_PTR(bridge_speed);
|
||||
OPT_PTR(ensure_vertical_shell_thickness);
|
||||
OPT_PTR(enforce_full_fill_volume);
|
||||
OPT_PTR(external_infill_margin);
|
||||
OPT_PTR(bridged_infill_margin);
|
||||
OPT_PTR(external_perimeter_extrusion_width);
|
||||
OPT_PTR(external_perimeter_speed);
|
||||
OPT_PTR(external_perimeters_first);
|
||||
@ -1109,6 +1109,11 @@ class SLAPrintObjectConfig : public StaticPrintConfig
|
||||
public:
|
||||
ConfigOptionFloat layer_height;
|
||||
|
||||
//Number of the layers needed for the exposure time fade [3;20]
|
||||
ConfigOptionInt faded_layers /*= 10*/;
|
||||
|
||||
ConfigOptionFloat slice_closing_radius;
|
||||
|
||||
// Enabling or disabling support creation
|
||||
ConfigOptionBool supports_enable;
|
||||
|
||||
@ -1153,9 +1158,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) /////////////////////////////////////////////
|
||||
|
||||
@ -1175,10 +1179,15 @@ public:
|
||||
// The smoothing radius of the pad edges
|
||||
ConfigOptionFloat pad_edge_radius /*= 1*/;
|
||||
|
||||
// The slope of the pad wall...
|
||||
ConfigOptionFloat pad_wall_slope;
|
||||
|
||||
protected:
|
||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||
{
|
||||
OPT_PTR(layer_height);
|
||||
OPT_PTR(faded_layers);
|
||||
OPT_PTR(slice_closing_radius);
|
||||
OPT_PTR(supports_enable);
|
||||
OPT_PTR(support_head_front_diameter);
|
||||
OPT_PTR(support_head_penetration);
|
||||
@ -1191,15 +1200,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_slope);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1236,6 +1245,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)
|
||||
{
|
||||
@ -1248,6 +1260,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,37 @@ 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";
|
||||
"usedMaterial = " + used_material + "\n"
|
||||
"numSlow = " + cnt_slow_layers + "\n"
|
||||
"numFast = " + cnt_fast_layers + "\n";
|
||||
}
|
||||
|
||||
public:
|
||||
@ -277,6 +293,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]);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -482,7 +482,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
||||
opt_key == "layer_height"
|
||||
|| opt_key == "first_layer_height"
|
||||
|| opt_key == "exact_last_layer_height"
|
||||
|| opt_key == "raft_layers") {
|
||||
|| opt_key == "raft_layers"
|
||||
|| opt_key == "slice_closing_radius") {
|
||||
steps.emplace_back(posSlice);
|
||||
}
|
||||
else if (
|
||||
@ -2010,7 +2011,7 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
|
||||
const Print *print = this->print();
|
||||
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
|
||||
mslicer.init(&mesh, callback);
|
||||
mslicer.slice(z, &layers, callback);
|
||||
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
|
||||
m_print->throw_if_canceled();
|
||||
}
|
||||
}
|
||||
|
@ -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,473 @@ 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)));
|
||||
const float slope_angle = 75.f * (float(M_PI)/180.f); // smaller number - less supports
|
||||
const float slope_offset = float(scale_(layer_height / std::tan(slope_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.overhangs_slopes = diff_ex(top_polygons, offset(bottom_polygons, slope_offset));
|
||||
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_slopes.empty()) {
|
||||
//FIXME add the support force deficit as a parameter, only cover until the defficiency is covered.
|
||||
uniformly_cover(s.overhangs_slopes, 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 +528,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,188 @@ 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; }
|
||||
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<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
|
||||
// Overhangs, that are dangling considerably.
|
||||
ExPolygons dangling_areas;
|
||||
// Complete overhands.
|
||||
ExPolygons overhangs;
|
||||
// Overhangs, where the surface must slope.
|
||||
ExPolygons overhangs_slopes;
|
||||
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;
|
||||
@ -236,10 +346,10 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
||||
// we use sin for x distance because we interpret the angle starting from
|
||||
// PI/2
|
||||
int tos = degrees < 90?
|
||||
int(radius_mm*std::cos(degrees * PI / 180 - PI/2) / stepx) : steps;
|
||||
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
|
||||
@ -446,7 +561,7 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
|
||||
heights.emplace_back(hi);
|
||||
|
||||
std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
|
||||
slicer.slice(heights, &out, thrfn);
|
||||
slicer.slice(heights, 0.f, &out, thrfn);
|
||||
|
||||
size_t count = 0; for(auto& o : out) count += o.size();
|
||||
|
||||
@ -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 wingdist = wingheight / std::tan(tilt);
|
||||
const double slope = cfg.wall_slope;
|
||||
const double wingdist = wingheight / std::tan(slope);
|
||||
|
||||
// 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,
|
||||
r,
|
||||
phi,
|
||||
0, // z position of the input plane
|
||||
true,
|
||||
thrcl,
|
||||
ob, wh);
|
||||
pool.merge(walledges);
|
||||
pool.merge(round_edges(ob,
|
||||
r,
|
||||
phi,
|
||||
0, // z position of the input plane
|
||||
true,
|
||||
thrcl,
|
||||
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,
|
||||
r,
|
||||
phi - 90, // from tangent lines
|
||||
0,
|
||||
false,
|
||||
thrcl,
|
||||
ob, wh);
|
||||
pool.merge(cavityedges);
|
||||
pool.merge(round_edges(middle_base,
|
||||
r,
|
||||
phi - 90, // from tangent lines
|
||||
0, // z position of the input plane
|
||||
false,
|
||||
thrcl,
|
||||
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);
|
||||
}
|
||||
|
||||
out.merge(mesh(pool));
|
||||
}
|
||||
|
||||
// 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_slope = 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 slope):
|
||||
min_wall_thickness_mm(wt),
|
||||
min_wall_height_mm(wh),
|
||||
max_merge_distance_mm(md),
|
||||
edge_radius_mm(er) {}
|
||||
edge_radius_mm(er),
|
||||
wall_slope(slope) {}
|
||||
};
|
||||
|
||||
/// 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};
|
||||
|
145
src/libslic3r/SLA/SLACommon.hpp
Normal file
@ -0,0 +1,145 @@
|
||||
#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 {
|
||||
|
||||
// An enum to keep track of where the current points on the ModelObject came from.
|
||||
enum class PointsStatus {
|
||||
None, // No points were generated so far.
|
||||
Generating, // The autogeneration algorithm triggered, but not yet finished.
|
||||
AutoGenerated, // Points were autogenerated (i.e. copied from the backend).
|
||||
UserModified // User has done some edits.
|
||||
};
|
||||
|
||||
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
|
@ -12,6 +12,7 @@
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
#include <libslic3r/Model.hpp>
|
||||
|
||||
#include <libnest2d/optimizers/nlopt/simplex.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <tbb/parallel_for.h>
|
||||
|
||||
@ -550,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;
|
||||
}
|
||||
|
||||
@ -588,7 +595,7 @@ double pinhead_mesh_intersect(const Vec3d& s,
|
||||
double r_back,
|
||||
double width,
|
||||
const EigenMesh3D& m,
|
||||
unsigned samples = 8,
|
||||
unsigned samples = 16,
|
||||
double safety_distance = 0.001)
|
||||
{
|
||||
// method based on:
|
||||
@ -614,9 +621,20 @@ double pinhead_mesh_intersect(const Vec3d& s,
|
||||
std::vector<double> phis(samples);
|
||||
for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size();
|
||||
|
||||
a(Z) = -(v(X)*a(X) + v(Y)*a(Y)) / v(Z);
|
||||
|
||||
b = a.cross(v);
|
||||
// We have to address the case when the direction vector v (same as dir)
|
||||
// is coincident with one of the world axes. In this case two of its
|
||||
// components will be completely zero and one is 1.0. Our method becomes
|
||||
// dangerous here due to division with zero. Instead, vector 'a' can be an
|
||||
// element-wise rotated version of 'v'
|
||||
auto chk1 = [] (double val) { return std::abs(std::abs(val) - 1) < 1e-20; };
|
||||
if(chk1(v(X)) || chk1(v(Y)) || chk1(v(Z))) {
|
||||
a = {v(Z), v(X), v(Y)};
|
||||
b = {v(Y), v(Z), v(X)};
|
||||
}
|
||||
else {
|
||||
a(Z) = -(v(Y)*a(Y)) / v(Z); a.normalize();
|
||||
b = a.cross(v);
|
||||
}
|
||||
|
||||
// Now a and b vectors are perpendicular to v and to each other. Together
|
||||
// they define the plane where we have to iterate with the given angles
|
||||
@ -641,18 +659,14 @@ double pinhead_mesh_intersect(const Vec3d& s,
|
||||
s(Z) + rpscos * a(Z) + rpssin * b(Z));
|
||||
|
||||
// Point ps is not on mesh but can be inside or outside as well. This
|
||||
// would cause many problems with ray-casting. So we query the closest
|
||||
// point on the mesh to this.
|
||||
// auto psq = m.signed_distance(ps);
|
||||
// would cause many problems with ray-casting. To detect the position we
|
||||
// will use the ray-casting result (which has an is_inside predicate).
|
||||
|
||||
// This is the point on the circle on the back sphere
|
||||
Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X),
|
||||
c(Y) + rpbcos * a(Y) + rpbsin * b(Y),
|
||||
c(Z) + rpbcos * a(Z) + rpbsin * b(Z));
|
||||
|
||||
// Vec3d n = (p - psq.point_on_mesh()).normalized();
|
||||
// phi = m.query_ray_hit(psq.point_on_mesh() + sd*n, n);
|
||||
|
||||
Vec3d n = (p - ps).normalized();
|
||||
auto hr = m.query_ray_hit(ps + sd*n, n);
|
||||
|
||||
@ -671,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.
|
||||
@ -686,8 +701,18 @@ double bridge_mesh_intersect(const Vec3d& s,
|
||||
Vec3d a(0, 1, 0), b;
|
||||
const double& sd = safety_distance;
|
||||
|
||||
a(Z) = -(dir(X)*a(X) + dir(Y)*a(Y)) / dir(Z);
|
||||
b = a.cross(dir);
|
||||
// INFO: for explanation of the method used here, see the previous method's
|
||||
// comments.
|
||||
|
||||
auto chk1 = [] (double val) { return std::abs(std::abs(val) - 1) < 1e-20; };
|
||||
if(chk1(dir(X)) || chk1(dir(Y)) || chk1(dir(Z))) {
|
||||
a = {dir(Z), dir(X), dir(Y)};
|
||||
b = {dir(Y), dir(Z), dir(X)};
|
||||
}
|
||||
else {
|
||||
a(Z) = -(dir(Y)*a(Y)) / dir(Z); a.normalize();
|
||||
b = a.cross(dir);
|
||||
}
|
||||
|
||||
// circle portions
|
||||
std::vector<double> phis(samples);
|
||||
@ -1149,16 +1174,16 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
//...
|
||||
};
|
||||
|
||||
// t-hrow i-f c-ance-l-ed: It will be called many times so a shorthand will
|
||||
// throw if canceled: It will be called many times so a shorthand will
|
||||
// come in handy.
|
||||
auto& tifcl = ctl.cancelfn;
|
||||
auto& thr = ctl.cancelfn;
|
||||
|
||||
// Filtering step: here we will discard inappropriate support points and
|
||||
// decide the future of the appropriate ones. We will check if a pinhead
|
||||
// is applicable and adjust its angle at each support point.
|
||||
// We will also merge the support points that are just too close and can be
|
||||
// considered as one.
|
||||
auto filterfn = [tifcl] (
|
||||
auto filterfn = [thr] (
|
||||
const SupportConfig& cfg,
|
||||
const PointSet& points,
|
||||
const EigenMesh3D& mesh,
|
||||
@ -1172,9 +1197,9 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
// first one
|
||||
auto aliases =
|
||||
cluster(points,
|
||||
[tifcl](const SpatElement& p, const SpatElement& se)
|
||||
[thr](const SpatElement& p, const SpatElement& se)
|
||||
{
|
||||
tifcl();
|
||||
thr();
|
||||
return distance(p.first, se.first) < D_SP;
|
||||
}, 2);
|
||||
|
||||
@ -1185,10 +1210,10 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
filt_pts.row(count++) = points.row(a.front());
|
||||
}
|
||||
|
||||
tifcl();
|
||||
thr();
|
||||
|
||||
// calculate the normals to the triangles belonging to filtered points
|
||||
auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, tifcl);
|
||||
auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, thr);
|
||||
|
||||
head_norm.resize(count, 3);
|
||||
head_pos.resize(count, 3);
|
||||
@ -1200,9 +1225,15 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
// not be enough space for the pinhead. Filtering is applied for
|
||||
// these reasons.
|
||||
|
||||
using libnest2d::opt::bound;
|
||||
using libnest2d::opt::initvals;
|
||||
using libnest2d::opt::SimplexOptimizer;
|
||||
using libnest2d::opt::StopCriteria;
|
||||
static const unsigned MAX_TRIES = 100;
|
||||
|
||||
int pcount = 0, hlcount = 0;
|
||||
for(int i = 0; i < count; i++) {
|
||||
tifcl();
|
||||
thr();
|
||||
auto n = nmls.row(i);
|
||||
|
||||
// for all normals we generate the spherical coordinates and
|
||||
@ -1223,32 +1254,67 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
// We saturate the polar angle to 3pi/4
|
||||
polar = std::max(polar, 3*PI / 4);
|
||||
|
||||
// Reassemble the now corrected normal
|
||||
Vec3d nn(std::cos(azimuth) * std::sin(polar),
|
||||
std::sin(azimuth) * std::sin(polar),
|
||||
std::cos(polar));
|
||||
|
||||
nn.normalize();
|
||||
|
||||
// save the head (pinpoint) position
|
||||
Vec3d hp = filt_pts.row(i);
|
||||
|
||||
// the full width of the head
|
||||
double w = cfg.head_width_mm +
|
||||
cfg.head_back_radius_mm +
|
||||
2*cfg.head_front_radius_mm;
|
||||
|
||||
// We should shoot a ray in the direction of the pinhead and
|
||||
// see if there is enough space for it
|
||||
double t = pinhead_mesh_intersect(
|
||||
hp, // touching point
|
||||
nn,
|
||||
cfg.head_front_radius_mm, // approx the radius
|
||||
cfg.head_back_radius_mm,
|
||||
w,
|
||||
mesh);
|
||||
// Reassemble the now corrected normal
|
||||
auto nn = Vec3d(std::cos(azimuth) * std::sin(polar),
|
||||
std::sin(azimuth) * std::sin(polar),
|
||||
std::cos(polar)).normalized();
|
||||
|
||||
if(t > w || std::isinf(t)) {
|
||||
// check available distance
|
||||
double t = pinhead_mesh_intersect(
|
||||
hp, // touching point
|
||||
nn, // normal
|
||||
cfg.head_front_radius_mm,
|
||||
cfg.head_back_radius_mm,
|
||||
w,
|
||||
mesh);
|
||||
|
||||
if(t <= w) {
|
||||
// Let's try to optimize this angle, there might be a viable
|
||||
// normal that doesn't collide with the model geometry and
|
||||
// its very close to the default.
|
||||
|
||||
StopCriteria stc;
|
||||
stc.max_iterations = MAX_TRIES;
|
||||
stc.relative_score_difference = 1e-3;
|
||||
stc.stop_score = w; // space greater than w is enough
|
||||
SimplexOptimizer solver(stc);
|
||||
|
||||
auto oresult = solver.optimize_max(
|
||||
[&mesh, &cfg, w, hp](double plr, double azm)
|
||||
{
|
||||
auto n = Vec3d(std::cos(azm) * std::sin(plr),
|
||||
std::sin(azm) * std::sin(plr),
|
||||
std::cos(plr)).normalized();
|
||||
|
||||
double score = pinhead_mesh_intersect(
|
||||
hp, n,
|
||||
cfg.head_front_radius_mm,
|
||||
cfg.head_back_radius_mm,
|
||||
w,
|
||||
mesh);
|
||||
return score;
|
||||
},
|
||||
initvals(polar, azimuth), // let's start with what we have
|
||||
bound(3*PI/4, PI), // Must not exceed the tilt limit
|
||||
bound(-PI, PI) // azimuth can be a full range search
|
||||
);
|
||||
|
||||
t = oresult.score;
|
||||
polar = std::get<0>(oresult.optimum);
|
||||
azimuth = std::get<1>(oresult.optimum);
|
||||
nn = Vec3d(std::cos(azimuth) * std::sin(polar),
|
||||
std::sin(azimuth) * std::sin(polar),
|
||||
std::cos(polar)).normalized();
|
||||
}
|
||||
|
||||
if(t > w) {
|
||||
head_pos.row(pcount) = hp;
|
||||
|
||||
// save the verified and corrected normal
|
||||
@ -1256,6 +1322,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
|
||||
++pcount;
|
||||
} else if( polar >= 3*PI/4 ) {
|
||||
|
||||
// Headless supports do not tilt like the headed ones so
|
||||
// the normal should point almost to the ground.
|
||||
headless_norm.row(hlcount) = nn;
|
||||
@ -1272,7 +1339,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
|
||||
// Pinhead creation: based on the filtering results, the Head objects will
|
||||
// be constructed (together with their triangle meshes).
|
||||
auto pinheadfn = [tifcl] (
|
||||
auto pinheadfn = [thr] (
|
||||
const SupportConfig& cfg,
|
||||
PointSet& head_pos,
|
||||
PointSet& nmls,
|
||||
@ -1285,7 +1352,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
/* ******************************************************** */
|
||||
|
||||
for (int i = 0; i < head_pos.rows(); ++i) {
|
||||
tifcl();
|
||||
thr();
|
||||
result.add_head(
|
||||
cfg.head_back_radius_mm,
|
||||
cfg.head_front_radius_mm,
|
||||
@ -1304,7 +1371,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
// will process it. Also, the pillars will be grouped into clusters that can
|
||||
// be interconnected with bridges. Elements of these groups may or may not
|
||||
// be interconnected. Here we only run the clustering algorithm.
|
||||
auto classifyfn = [tifcl] (
|
||||
auto classifyfn = [thr] (
|
||||
const SupportConfig& cfg,
|
||||
const EigenMesh3D& mesh,
|
||||
PointSet& head_pos,
|
||||
@ -1313,7 +1380,8 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
std::vector<double>& gndheight,
|
||||
ClusteredPoints& ground_clusters,
|
||||
Result& result
|
||||
) {
|
||||
)
|
||||
{
|
||||
|
||||
/* ******************************************************** */
|
||||
/* Classification */
|
||||
@ -1324,11 +1392,11 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
gndidx.reserve(size_t(head_pos.rows()));
|
||||
nogndidx.reserve(size_t(head_pos.rows()));
|
||||
|
||||
// First we search decide which heads reach the ground and can be full
|
||||
// First we decide which heads reach the ground and can be full
|
||||
// pillars and which shall be connected to the model surface (or search
|
||||
// a suitable path around the surface that leads to the ground -- TODO)
|
||||
for(unsigned i = 0; i < head_pos.rows(); i++) {
|
||||
tifcl();
|
||||
thr();
|
||||
auto& head = result.head(i);
|
||||
|
||||
Vec3d dir(0, 0, -1);
|
||||
@ -1337,9 +1405,38 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
double t = std::numeric_limits<double>::infinity();
|
||||
double hw = head.width_mm;
|
||||
|
||||
|
||||
{
|
||||
// using libnest2d::opt::Method;
|
||||
// using libnest2d::opt::bound;
|
||||
// using libnest2d::opt::Optimizer;
|
||||
// using libnest2d::opt::TOptimizer;
|
||||
// using libnest2d::opt::StopCriteria;
|
||||
|
||||
// auto stopcond = [] () { return false; };
|
||||
// static const unsigned max_tries = 100;
|
||||
|
||||
// auto objfunc =
|
||||
// [&head](double polar, double azimuth, double width)
|
||||
// {
|
||||
// Vec3d nn(std::cos(azimuth) * std::sin(polar),
|
||||
// std::sin(azimuth) * std::sin(polar),
|
||||
// std::cos(polar));
|
||||
|
||||
|
||||
// };
|
||||
|
||||
// StopCriteria stc;
|
||||
// stc.max_iterations = max_tries;
|
||||
// stc.relative_score_difference = 1e-3;
|
||||
// stc.stop_condition = stopcond;
|
||||
// TOptimizer<Method::L_SIMPLEX> solver(stc);
|
||||
}
|
||||
|
||||
|
||||
// We will try to assign a pillar to all the pinheads. If a pillar
|
||||
// would pierce the model surface, we will try to adjust slightly
|
||||
// the head with so that the pillar can be deployed.
|
||||
// the head width so that the pillar can be deployed.
|
||||
while(!accept && head.width_mm > 0) {
|
||||
|
||||
Vec3d startpoint = head.junction_point();
|
||||
@ -1351,11 +1448,18 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
double tprec = ray_mesh_intersect(startpoint, dir, mesh);
|
||||
|
||||
if(std::isinf(tprec) && !std::isinf(t)) {
|
||||
// This is a damned case where the pillar melds into the
|
||||
// This is a damned case where the pillar melts into the
|
||||
// model but its center ray can reach the ground. We can
|
||||
// not route this to the ground nor to the model surface.
|
||||
head.width_mm = hw + (ri % 2? -1 : 1) * ri * head.r_back_mm;
|
||||
} else {
|
||||
if(!std::isinf(t) && !std::isinf(tprec) &&
|
||||
std::abs(tprec - t) > hw)
|
||||
{
|
||||
// In this case the head would scratch the model body
|
||||
BOOST_LOG_TRIVIAL(warning) << "Head scratch detected.";
|
||||
}
|
||||
|
||||
accept = true; t = tprec;
|
||||
|
||||
auto id = head.id;
|
||||
@ -1410,9 +1514,9 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
ground_clusters =
|
||||
cluster(
|
||||
gnd,
|
||||
[d_base, tifcl](const SpatElement& p, const SpatElement& s)
|
||||
[d_base, thr](const SpatElement& p, const SpatElement& s)
|
||||
{
|
||||
tifcl();
|
||||
thr();
|
||||
return distance(Vec2d(p.first(X), p.first(Y)),
|
||||
Vec2d(s.first(X), s.first(Y))) < d_base;
|
||||
}, 3); // max 3 heads to connect to one centroid
|
||||
@ -1485,7 +1589,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
// a full pillar (ground connected). Some will connect to a nearby pillar
|
||||
// using a bridge. The max number of such side-heads for a central pillar
|
||||
// is limited to avoid bad weight distribution.
|
||||
auto routing_ground_fn = [gnd_head_pt, interconnect, tifcl](
|
||||
auto routing_ground_fn = [gnd_head_pt, interconnect, thr](
|
||||
const SupportConfig& cfg,
|
||||
const ClusteredPoints& gnd_clusters,
|
||||
const IndexSet& gndidx,
|
||||
@ -1501,7 +1605,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
cl_centroids.reserve(gnd_clusters.size());
|
||||
|
||||
SpatIndex pheadindex; // spatial index for the junctions
|
||||
for(auto& cl : gnd_clusters) { tifcl();
|
||||
for(auto& cl : gnd_clusters) { thr();
|
||||
// place all the centroid head positions into the index. We will
|
||||
// query for alternative pillar positions. If a sidehead cannot
|
||||
// connect to the cluster centroid, we have to search for another
|
||||
@ -1512,9 +1616,9 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
|
||||
// get the current cluster centroid
|
||||
long lcid = cluster_centroid(cl, gnd_head_pt,
|
||||
[tifcl](const Vec3d& p1, const Vec3d& p2)
|
||||
[thr](const Vec3d& p1, const Vec3d& p2)
|
||||
{
|
||||
tifcl();
|
||||
thr();
|
||||
return distance(Vec2d(p1(X), p1(Y)), Vec2d(p2(X), p2(Y)));
|
||||
});
|
||||
|
||||
@ -1535,7 +1639,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
// sidepoints with the cluster centroid (which is a ground pillar)
|
||||
// or a nearby pillar if the centroid is unreachable.
|
||||
size_t ci = 0;
|
||||
for(auto cl : gnd_clusters) { tifcl();
|
||||
for(auto cl : gnd_clusters) { thr();
|
||||
|
||||
auto cidx = cl_centroids[ci];
|
||||
cl_centroids[ci++] = cl[cidx];
|
||||
@ -1559,12 +1663,12 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
// is distributed more effectively on the pillar.
|
||||
|
||||
auto search_nearest =
|
||||
[&tifcl, &cfg, &result, &emesh, maxbridgelen, gndlvl, pradius]
|
||||
[&thr, &cfg, &result, &emesh, maxbridgelen, gndlvl, pradius]
|
||||
(SpatIndex& spindex, const Vec3d& jsh)
|
||||
{
|
||||
long nearest_id = -1;
|
||||
const double max_len = maxbridgelen / 2;
|
||||
while(nearest_id < 0 && !spindex.empty()) { tifcl();
|
||||
while(nearest_id < 0 && !spindex.empty()) { thr();
|
||||
// loop until a suitable head is not found
|
||||
// if there is a pillar closer than the cluster center
|
||||
// (this may happen as the clustering is not perfect)
|
||||
@ -1603,7 +1707,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
return nearest_id;
|
||||
};
|
||||
|
||||
for(auto c : cl) { tifcl();
|
||||
for(auto c : cl) { thr();
|
||||
auto& sidehead = result.head(gndidx[c]);
|
||||
sidehead.transform();
|
||||
|
||||
@ -1669,7 +1773,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
ClusterEl ring;
|
||||
|
||||
while(!rem.empty()) { // loop until all the points belong to some ring
|
||||
tifcl();
|
||||
thr();
|
||||
std::sort(rem.begin(), rem.end());
|
||||
|
||||
auto newring = pts_convex_hull(rem,
|
||||
@ -1681,7 +1785,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
if(!ring.empty()) {
|
||||
// inner ring is now in 'newring' and outer ring is in 'ring'
|
||||
SpatIndex innerring;
|
||||
for(unsigned i : newring) { tifcl();
|
||||
for(unsigned i : newring) { thr();
|
||||
const Pillar& pill = result.head_pillar(gndidx[i]);
|
||||
assert(pill.id >= 0);
|
||||
innerring.insert(pill.endpoint, unsigned(pill.id));
|
||||
@ -1690,7 +1794,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
// For all pillars in the outer ring find the closest in the
|
||||
// inner ring and connect them. This will create the spider web
|
||||
// fashioned connections between pillars
|
||||
for(unsigned i : ring) { tifcl();
|
||||
for(unsigned i : ring) { thr();
|
||||
const Pillar& outerpill = result.head_pillar(gndidx[i]);
|
||||
auto res = innerring.nearest(outerpill.endpoint, 1);
|
||||
if(res.empty()) continue;
|
||||
@ -1716,7 +1820,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
next != ring.end();
|
||||
++it, ++next)
|
||||
{
|
||||
tifcl();
|
||||
thr();
|
||||
const Pillar& pillar = result.head_pillar(gndidx[*it]);
|
||||
const Pillar& nextpillar = result.head_pillar(gndidx[*next]);
|
||||
interconnect(pillar, nextpillar, emesh, result);
|
||||
@ -1731,19 +1835,19 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
}
|
||||
};
|
||||
|
||||
// Step: routing the pinheads that are would connect to the model surface
|
||||
// Step: routing the pinheads that would connect to the model surface
|
||||
// along the Z axis downwards. For now these will actually be connected with
|
||||
// the model surface with a flipped pinhead. In the future here we could use
|
||||
// some smart algorithms to search for a safe path to the ground or to a
|
||||
// nearby pillar that can hold the supported weight.
|
||||
auto routing_nongnd_fn = [tifcl](
|
||||
auto routing_nongnd_fn = [thr](
|
||||
const SupportConfig& cfg,
|
||||
const std::vector<double>& gndheight,
|
||||
const IndexSet& nogndidx,
|
||||
Result& result)
|
||||
{
|
||||
// TODO: connect these to the ground pillars if possible
|
||||
for(auto idx : nogndidx) { tifcl();
|
||||
for(auto idx : nogndidx) { thr();
|
||||
double gh = gndheight[idx];
|
||||
double base_width = cfg.head_width_mm;
|
||||
|
||||
@ -1800,7 +1904,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
// Step: process the support points where there is not enough space for a
|
||||
// full pinhead. In this case we will use a rounded sphere as a touching
|
||||
// point and use a thinner bridge (let's call it a stick).
|
||||
auto process_headless = [tifcl](
|
||||
auto process_headless = [thr](
|
||||
const SupportConfig& cfg,
|
||||
const PointSet& headless_pts,
|
||||
const PointSet& headless_norm,
|
||||
@ -1815,7 +1919,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
||||
|
||||
// We will sink the pins into the model surface for a distance of 1/3 of
|
||||
// the pin radius
|
||||
for(int i = 0; i < headless_pts.rows(); i++) { tifcl();
|
||||
for(int i = 0; i < headless_pts.rows(); i++) { thr();
|
||||
Vec3d sph = headless_pts.row(i); // Exact support position
|
||||
Vec3d n = headless_norm.row(i); // mesh outward normal
|
||||
Vec3d sp = sph - n * HWIDTH_MM; // stick head start point
|
||||
@ -2001,7 +2105,7 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
|
||||
fullmesh.merge(get_pad());
|
||||
TriangleMeshSlicer slicer(&fullmesh);
|
||||
SlicedSupports ret;
|
||||
slicer.slice(heights, &ret, get().ctl().cancelfn);
|
||||
slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -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) {
|
||||
@ -335,11 +337,34 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
||||
}
|
||||
}
|
||||
}
|
||||
if (model_object.sla_support_points != model_object_new.sla_support_points) {
|
||||
/*if (model_object.sla_support_points != model_object_new.sla_support_points) {
|
||||
model_object.sla_support_points = model_object_new.sla_support_points;
|
||||
if (it_print_object_status != print_object_status.end())
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
||||
}
|
||||
if (model_object.sla_points_status != model_object_new.sla_points_status) {
|
||||
// Change of this status should invalidate support points. The points themselves are not enough, there are none
|
||||
// in case that nothing was generated OR that points were autogenerated already and not copied to the front-end.
|
||||
// These cases can only be differentiated by checking the status change. However, changing from 'Generating' should NOT
|
||||
// invalidate - that would keep stopping the background processing without a reason.
|
||||
if (model_object.sla_points_status != sla::PointsStatus::Generating)
|
||||
if (it_print_object_status != print_object_status.end())
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
||||
model_object.sla_points_status = model_object_new.sla_points_status;
|
||||
}*/
|
||||
|
||||
bool old_user_modified = model_object.sla_points_status == sla::PointsStatus::UserModified;
|
||||
bool new_user_modified = model_object_new.sla_points_status == sla::PointsStatus::UserModified;
|
||||
if ((old_user_modified && ! new_user_modified) || // switching to automatic supports from manual supports
|
||||
(! old_user_modified && new_user_modified) || // switching to manual supports from automatic supports
|
||||
(new_user_modified && model_object.sla_support_points != model_object_new.sla_support_points)) {
|
||||
if (it_print_object_status != print_object_status.end())
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
||||
|
||||
model_object.sla_points_status = model_object_new.sla_points_status;
|
||||
model_object.sla_support_points = model_object_new.sla_support_points;
|
||||
}
|
||||
|
||||
// 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;
|
||||
@ -354,14 +379,18 @@ 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 != it_print_object_status->print_object->instances()) {
|
||||
// Instances changed.
|
||||
it_print_object_status->print_object->set_instances(new_instances);
|
||||
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||
}
|
||||
print_objects_new.emplace_back(it_print_object_status->print_object);
|
||||
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Reused;
|
||||
} else {
|
||||
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);
|
||||
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||
}
|
||||
print_objects_new.emplace_back(it_print_object_status->print_object);
|
||||
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Reused;
|
||||
}
|
||||
} else if (! new_instances.empty()) {
|
||||
auto print_object = new SLAPrintObject(this, &model_object);
|
||||
|
||||
// FIXME: this invalidates the transformed mesh in SLAPrintObject
|
||||
@ -376,7 +405,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 +427,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, "sl1", &config);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Compile the argument for support creation from the static print config.
|
||||
sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
|
||||
@ -472,7 +608,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
|
||||
@ -501,7 +637,7 @@ void SLAPrint::process()
|
||||
ilh, float(lh));
|
||||
|
||||
auto& layers = po.m_model_slices; layers.clear();
|
||||
slicer.slice(heights, &layers, [this](){ throw_if_canceled(); });
|
||||
slicer.slice(heights, float(po.config().slice_closing_radius.value), &layers, [this](){ throw_if_canceled(); });
|
||||
};
|
||||
|
||||
// In this step we check the slices, identify island and cover them with
|
||||
@ -517,10 +653,11 @@ void SLAPrint::process()
|
||||
BOOST_LOG_TRIVIAL(debug) << "Support point count "
|
||||
<< mo.sla_support_points.size();
|
||||
|
||||
// If there are no points on the front-end, we will do the
|
||||
// autoplacement. Otherwise we will just blindly copy the frontend data
|
||||
// Unless the user modified the points or we already did the calculation, we will do
|
||||
// the autoplacement. Otherwise we will just blindly copy the frontend data
|
||||
// into the backend cache.
|
||||
if(mo.sla_support_points.empty()) {
|
||||
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
|
||||
|
||||
// calculate heights of slices (slices are calculated already)
|
||||
double lh = po.m_config.layer_height.getFloat();
|
||||
|
||||
@ -532,9 +669,10 @@ 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;
|
||||
|
||||
// the density config value is in percents:
|
||||
config.density_relative = float(cfg.support_points_density_relative / 100.f);
|
||||
config.minimal_distance = float(cfg.support_points_minimal_distance);
|
||||
|
||||
// Construction of this object does the calculation.
|
||||
this->throw_if_canceled();
|
||||
@ -546,17 +684,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());
|
||||
// There are either some points on the front-end, or the user removed them on purpose. No calculation will be done.
|
||||
po.m_supportdata->support_points = po.transformed_support_points();
|
||||
}
|
||||
};
|
||||
|
||||
@ -587,6 +727,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 +736,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 +747,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 +777,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_slope.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 +794,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 +1021,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,28 +1058,32 @@ void SLAPrint::process()
|
||||
|
||||
// TODO: this loop could run in parallel but should not exhaust all the CPU
|
||||
// power available
|
||||
for(SLAPrintObject * po : m_objects) {
|
||||
// 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;
|
||||
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
|
||||
// execution gets to this point and throws the canceled signal.
|
||||
throw_if_canceled();
|
||||
|
||||
st += unsigned(incr * ostepd);
|
||||
|
||||
if(po->m_stepmask[currentstep] && po->set_started(currentstep)) {
|
||||
report_status(*this, int(st), OBJ_STEP_LABELS[currentstep]);
|
||||
pobj_program[currentstep](*po);
|
||||
// Cancellation checking. Each step will check for cancellation
|
||||
// on its own and return earlier gracefully. Just after it returns
|
||||
// execution gets to this point and throws the canceled signal.
|
||||
throw_if_canceled();
|
||||
po->set_done(currentstep);
|
||||
}
|
||||
|
||||
incr = OBJ_STEP_LEVELS[currentstep];
|
||||
st += unsigned(incr * ostepd);
|
||||
|
||||
if(po->m_stepmask[currentstep] && po->set_started(currentstep)) {
|
||||
report_status(*this, int(st), OBJ_STEP_LABELS[currentstep]);
|
||||
pobj_program[currentstep](*po);
|
||||
throw_if_canceled();
|
||||
po->set_done(currentstep);
|
||||
}
|
||||
|
||||
incr = OBJ_STEP_LEVELS[currentstep];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -994,7 +1140,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 +1176,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 +1343,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 +1369,14 @@ 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"
|
||||
|| opt_key == "slice_closing_radius") {
|
||||
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 +1396,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_slope"
|
||||
|| opt_key == "pad_edge_radius") {
|
||||
steps.emplace_back(slaposBasePool);
|
||||
} else {
|
||||
@ -1165,7 +1480,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 +1559,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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -852,7 +852,7 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
|
||||
}
|
||||
}
|
||||
|
||||
void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
|
||||
void TriangleMeshSlicer::slice(const std::vector<float> &z, const float closing_radius, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
|
||||
{
|
||||
std::vector<Polygons> layers_p;
|
||||
this->slice(z, &layers_p, throw_on_cancel);
|
||||
@ -861,13 +861,13 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygo
|
||||
layers->resize(z.size());
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, z.size()),
|
||||
[&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
|
||||
[&layers_p, closing_radius, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
|
||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||
printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
|
||||
#endif
|
||||
throw_on_cancel();
|
||||
this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
|
||||
this->make_expolygons(layers_p[layer_id], closing_radius, &(*layers)[layer_id]);
|
||||
}
|
||||
});
|
||||
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
|
||||
@ -1600,7 +1600,7 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
|
||||
#endif
|
||||
}
|
||||
|
||||
void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const
|
||||
void TriangleMeshSlicer::make_expolygons(const Polygons &loops, const float closing_radius, ExPolygons* slices) const
|
||||
{
|
||||
/*
|
||||
Input loops are not suitable for evenodd nor nonzero fill types, as we might get
|
||||
@ -1655,7 +1655,7 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
|
||||
// 0.0499 comes from https://github.com/slic3r/Slic3r/issues/959
|
||||
// double safety_offset = scale_(0.0499);
|
||||
// 0.0001 is set to satisfy GH #520, #1029, #1364
|
||||
// double safety_offset = scale_(0.0001); // now a config value
|
||||
double safety_offset = scale_(closing_radius);
|
||||
|
||||
/* The following line is commented out because it can generate wrong polygons,
|
||||
see for example issue #661 */
|
||||
@ -1670,17 +1670,17 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
|
||||
#endif
|
||||
|
||||
// append to the supplied collection
|
||||
/* Fix for issue #661 { */
|
||||
if (safety_offset > 0)
|
||||
expolygons_append(*slices, offset2_ex(union_(loops, false), +safety_offset, -safety_offset));
|
||||
//expolygons_append(*slices, ex_slices);
|
||||
/* } */
|
||||
else
|
||||
expolygons_append(*slices, union_ex(loops, false));
|
||||
}
|
||||
|
||||
void TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
|
||||
void TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, const float closing_radius, ExPolygons* slices) const
|
||||
{
|
||||
Polygons pp;
|
||||
this->make_loops(lines, &pp);
|
||||
this->make_expolygons(pp, slices);
|
||||
this->make_expolygons(pp, closing_radius, slices);
|
||||
}
|
||||
|
||||
void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const
|
||||
@ -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(); ) {
|
||||
|
@ -165,7 +165,7 @@ public:
|
||||
TriangleMeshSlicer(TriangleMesh* mesh) { this->init(mesh, [](){}); }
|
||||
void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
|
||||
void slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
|
||||
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
|
||||
void slice(const std::vector<float> &z, const float closing_radius, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
|
||||
enum FacetSliceType {
|
||||
NoSlice = 0,
|
||||
Slicing = 1,
|
||||
@ -175,7 +175,6 @@ public:
|
||||
const float min_z, const float max_z, IntersectionLine *line_out) const;
|
||||
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
|
||||
|
||||
double safety_offset = scale_(0.02);
|
||||
private:
|
||||
const TriangleMesh *mesh;
|
||||
// Map from a facet to an edge index.
|
||||
@ -185,9 +184,9 @@ private:
|
||||
|
||||
void _slice_do(size_t facet_idx, std::vector<IntersectionLines>* lines, boost::mutex* lines_mutex, const std::vector<float> &z) const;
|
||||
void make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const;
|
||||
void make_expolygons(const Polygons &loops, ExPolygons* slices) const;
|
||||
void make_expolygons(const Polygons &loops, const float closing_radius, ExPolygons* slices) const;
|
||||
void make_expolygons_simple(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
|
||||
void make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const;
|
||||
void make_expolygons(std::vector<IntersectionLine> &lines, const float closing_radius, ExPolygons* slices) const;
|
||||
};
|
||||
|
||||
TriangleMesh make_cube(double x, double y, double z);
|
||||
|
@ -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
1452
src/nanosvg/nanosvgrast.h
Normal file
@ -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;
|
||||
|
809
src/slic3r/GUI/3DBed.cpp
Normal file
@ -0,0 +1,809 @@
|
||||
#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)
|
||||
{
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, m_texture.get_id()));
|
||||
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
|
||||
glsafe(::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())
|
||||
{
|
||||
glsafe(::glEnable(GL_LIGHTING));
|
||||
m_model.render();
|
||||
glsafe(::glDisable(GL_LIGHTING));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int triangles_vcount = m_triangles.get_vertices_count();
|
||||
if (triangles_vcount > 0)
|
||||
{
|
||||
if (m_vbo_id == 0)
|
||||
{
|
||||
glsafe(::glGenBuffers(1, &m_vbo_id));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
|
||||
glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
}
|
||||
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
glsafe(::glDepthMask(GL_FALSE));
|
||||
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
if (bottom)
|
||||
glsafe(::glFrontFace(GL_CW));
|
||||
|
||||
render_prusa_shader(bottom);
|
||||
|
||||
if (bottom)
|
||||
glsafe(::glFrontFace(GL_CCW));
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glDepthMask(GL_TRUE));
|
||||
}
|
||||
}
|
||||
|
||||
void Bed3D::render_prusa_shader(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);
|
||||
|
||||
unsigned int stride = m_triangles.get_vertex_data_size();
|
||||
|
||||
GLint position_id = m_shader.get_attrib_location("v_position");
|
||||
GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords");
|
||||
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_texture.get_id()));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
|
||||
|
||||
if (position_id != -1)
|
||||
{
|
||||
glsafe(::glEnableVertexAttribArray(position_id));
|
||||
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)m_triangles.get_position_offset()));
|
||||
}
|
||||
if (tex_coords_id != -1)
|
||||
{
|
||||
glsafe(::glEnableVertexAttribArray(tex_coords_id));
|
||||
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)m_triangles.get_tex_coords_offset()));
|
||||
}
|
||||
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));
|
||||
|
||||
if (tex_coords_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(tex_coords_id));
|
||||
|
||||
if (position_id != -1)
|
||||
glsafe(::glDisableVertexAttribArray(position_id));
|
||||
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
glsafe(::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
|
||||
glsafe(::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
|
||||
glsafe(::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)
|
||||
{
|
||||
glsafe(::glDeleteBuffers(1, &m_vbo_id));
|
||||
m_vbo_id = 0;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
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(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();
|
||||
@ -1667,27 +1663,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);
|
||||
}
|
||||
|
||||
@ -1721,13 +1709,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
|
||||
{
|
||||
@ -1964,7 +1950,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();
|
||||
@ -2007,7 +1992,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)
|
||||
{
|
||||
|
@ -457,7 +457,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
|
||||
@ -499,20 +499,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;
|
||||
@ -522,22 +518,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;
|
||||
@ -561,13 +551,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);
|
||||
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
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());
|
||||
|
@ -150,7 +150,7 @@ void BackgroundSlicingProcess::process_sla()
|
||||
if (this->set_step_started(bspsGCodeFinalize)) {
|
||||
if (! m_export_path.empty()) {
|
||||
m_sla_print->export_raster<SLAZipFmt>(m_export_path);
|
||||
m_print->set_status(100, "Zip file exported to " + m_export_path);
|
||||
m_print->set_status(100, "Masked SLA file exported to " + m_export_path);
|
||||
} else if (! m_upload_job.empty()) {
|
||||
prepare_upload();
|
||||
} else {
|
||||
@ -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);
|
||||
|
||||
|