Merged with dev
2
Build.PL
@ -12,6 +12,7 @@ my %prereqs = qw(
|
||||
ExtUtils::ParseXS 3.22
|
||||
ExtUtils::XSpp 0
|
||||
ExtUtils::Typemaps 0
|
||||
ExtUtils::Typemaps::Basic 0
|
||||
File::Basename 0
|
||||
File::Spec 0
|
||||
Getopt::Long 0
|
||||
@ -35,7 +36,6 @@ if ($gui) {
|
||||
%prereqs = qw(
|
||||
Class::Accessor 0
|
||||
Wx 0.9918
|
||||
Socket 2.016
|
||||
);
|
||||
%recommends = qw(
|
||||
Wx::GLCanvas 0
|
||||
|
@ -1,5 +1,4 @@
|
||||
# Boost 1.63 requires CMake 3.7 or newer
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
project(Slic3r)
|
||||
|
||||
@ -22,8 +21,8 @@ option(SLIC3R_STATIC "Compile Slic3r with static libraries (Boost, TBB, glew)
|
||||
option(SLIC3R_GUI "Compile Slic3r with GUI components (OpenGL, wxWidgets)" 1)
|
||||
option(SLIC3R_PRUSACONTROL "Compile Slic3r with the PrusaControl prject file format (requires wxWidgets base library)" 1)
|
||||
option(SLIC3R_PROFILE "Compile Slic3r with an invasive Shiny profiler" 0)
|
||||
option(SLIC3R_HAS_BROKEN_CROAK "Compile Slic3r for a broken Strawberry Perl 64bit" 0)
|
||||
option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
|
||||
option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
|
||||
|
||||
if (MSVC AND SLIC3R_MSVC_COMPILE_PARALLEL)
|
||||
add_compile_options(/MP)
|
||||
@ -37,10 +36,18 @@ else()
|
||||
set(ENV_PATH_SEPARATOR ":")
|
||||
endif()
|
||||
set(ENV{PATH} "${PROJECT_SOURCE_DIR}/local-lib/bin${ENV_PATH_SEPARATOR}$ENV{PATH}")
|
||||
set(ENV{PERL5LIB} "${PROJECT_SOURCE_DIR}/local-lib/lib/perl${ENV_PATH_SEPARATOR}$ENV{PERL5LIB}")
|
||||
set(PERL_INCLUDE "${PROJECT_SOURCE_DIR}/local-lib/lib/perl5${ENV_PATH_SEPARATOR}$ENV{PERL5LIB}")
|
||||
message("PATH: $ENV{PATH}")
|
||||
message("PERL5LIB: $ENV{PERL5LIB}")
|
||||
message("PERL_INCLUDE: ${PERL_INCLUDE}")
|
||||
find_package(Perl REQUIRED)
|
||||
if (WIN32)
|
||||
# On Windows passing the PERL5LIB variable causes various problems (such as with MAX_PATH and others),
|
||||
# basically I've found no good way to do it on Windows.
|
||||
set(PERL5LIB_ENV_CMD "")
|
||||
else()
|
||||
set(PERL5LIB_ENV_CMD ${CMAKE_COMMAND} -E env PERL5LIB=${PERL_INCLUDE})
|
||||
endif()
|
||||
|
||||
|
||||
# CMAKE_PREFIX_PATH is used to point CMake to the remaining dependencies (Boost, TBB, ...)
|
||||
# We pick it from environment if it is not defined in another way
|
||||
@ -50,6 +57,24 @@ if(NOT DEFINED CMAKE_PREFIX_PATH)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
enable_testing ()
|
||||
|
||||
# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory.
|
||||
# We pick it from environment if it is not defined in another way
|
||||
if(WIN32)
|
||||
if(NOT DEFINED WIN10SDK_PATH)
|
||||
if(DEFINED ENV{WIN10SDK_PATH})
|
||||
set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
if(DEFINED WIN10SDK_PATH AND NOT EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h")
|
||||
message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}")
|
||||
message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found")
|
||||
message("STL fixing by the Netfabb service will not be compiled")
|
||||
unset(WIN10SDK_PATH)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(xs)
|
||||
|
||||
get_filename_component(PERL_BIN_PATH "${PERL_EXECUTABLE}" DIRECTORY)
|
||||
@ -64,7 +89,6 @@ else ()
|
||||
set(PERL_PROVE "${PERL_BIN_PATH}/prove")
|
||||
endif ()
|
||||
|
||||
enable_testing ()
|
||||
add_test (NAME xs COMMAND "${PERL_EXECUTABLE}" ${PERL_PROVE} -I ${PROJECT_SOURCE_DIR}/local-lib/lib/perl5 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/xs)
|
||||
add_test (NAME integration COMMAND "${PERL_EXECUTABLE}" ${PERL_PROVE} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
|
||||
|
@ -128,6 +128,7 @@ The author of the Silk icon set is Mark James.
|
||||
--post-process Generated G-code will be processed with the supplied script;
|
||||
call this more than once to process through multiple scripts.
|
||||
--export-svg Export a SVG file containing slices instead of G-code.
|
||||
--export-png Export zipped PNG files containing slices instead of G-code.
|
||||
-m, --merge If multiple files are supplied, they will be composed into a single
|
||||
print rather than processed individually.
|
||||
|
||||
@ -385,4 +386,4 @@ If you want to change a preset file, just do
|
||||
|
||||
If you want to slice a file overriding an option contained in your preset file:
|
||||
|
||||
slic3r.pl --load config.ini --layer-height 0.25 file.stl
|
||||
slic3r.pl --load config.ini --layer-height 0.25 file.stl
|
||||
|
@ -49,6 +49,7 @@ my \$defines = ' ' . Alien::wxWidgets->defines;
|
||||
my \$cflags = Alien::wxWidgets->c_flags;
|
||||
my \$linkflags = Alien::wxWidgets->link_flags;
|
||||
my \$libraries = ' ' . Alien::wxWidgets->libraries(@components);
|
||||
my \$gui_toolkit = Alien::wxWidgets->config->{toolkit};
|
||||
#my @libraries = Alien::wxWidgets->link_libraries(@components);
|
||||
#my @implib = Alien::wxWidgets->import_libraries(@components);
|
||||
#my @shrlib = Alien::wxWidgets->shared_libraries(@components);
|
||||
@ -82,6 +83,7 @@ cmake_set_var('LIBRARIES', \$libraries);
|
||||
cmake_set_var('DEFINITIONS', \$defines);
|
||||
#cmake_set_var('DEFINITIONS_DEBUG', );
|
||||
cmake_set_var('CXX_FLAGS', \$cflags);
|
||||
cmake_set_var('GUI_TOOLKIT', \$gui_toolkit);
|
||||
close \$fh;
|
||||
")
|
||||
include(${AlienWx_TEMP_INCLUDE})
|
||||
@ -96,6 +98,7 @@ if (AlienWx_DEBUG)
|
||||
message(STATUS " AlienWx_DEFINITIONS = ${AlienWx_DEFINITIONS}")
|
||||
message(STATUS " AlienWx_DEFINITIONS_DEBUG = ${AlienWx_DEFINITIONS_DEBUG}")
|
||||
message(STATUS " AlienWx_CXX_FLAGS = ${AlienWx_CXX_FLAGS}")
|
||||
message(STATUS " AlienWx_GUI_TOOLKIT = ${AlienWx_GUI_TOOLKIT}")
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
28
cmake/modules/FindFlann.cmake
Normal file
@ -0,0 +1,28 @@
|
||||
###############################################################################
|
||||
# Find Flann
|
||||
#
|
||||
# This sets the following variables:
|
||||
# FLANN_FOUND - True if FLANN was found.
|
||||
# FLANN_INCLUDE_DIRS - Directories containing the FLANN include files.
|
||||
# FLANN_LIBRARIES - Libraries needed to use FLANN.
|
||||
# FLANN_DEFINITIONS - Compiler flags for FLANN.
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PC_FLANN flann)
|
||||
set(FLANN_DEFINITIONS ${PC_FLANN_CFLAGS_OTHER})
|
||||
|
||||
find_path(FLANN_INCLUDE_DIR flann/flann.hpp
|
||||
HINTS ${PC_FLANN_INCLUDEDIR} ${PC_FLANN_INCLUDE_DIRS})
|
||||
|
||||
find_library(FLANN_LIBRARY flann_cpp
|
||||
HINTS ${PC_FLANN_LIBDIR} ${PC_FLANN_LIBRARY_DIRS})
|
||||
|
||||
set(FLANN_INCLUDE_DIRS ${FLANN_INCLUDE_DIR})
|
||||
set(FLANN_LIBRARIES ${FLANN_LIBRARY})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Flann DEFAULT_MSG
|
||||
FLANN_LIBRARY FLANN_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(FLANN_LIBRARY FLANN_INCLUDE_DIR)
|
||||
|
@ -1,18 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is autogenerated by CMake
|
||||
Note: In the .in template file, the $ {}-style variables are interpreted by CMake while the $()-style variables belong to MSVC
|
||||
-->
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets">
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<ExecutablePath>$(VC_ExecutablePath_x64);$(WindowsSDK_ExecutablePath);$(VS_ExecutablePath);$(MSBuild_ExecutablePath);$(FxCopDir);$(PATH);c:\wperl64d\bin\;</ExecutablePath>
|
||||
<ExecutablePath>$(VC_ExecutablePath_x64);$(WindowsSDK_ExecutablePath);$(VS_ExecutablePath);$(MSBuild_ExecutablePath);$(FxCopDir);$(PATH);${PROPS_PERL_BIN_PATH}\;</ExecutablePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup />
|
||||
<ItemGroup />
|
||||
|
||||
<PropertyGroup>
|
||||
<LocalDebuggerCommand>C:\wperl64d\bin\perl.exe</LocalDebuggerCommand>
|
||||
<LocalDebuggerCommand>${PROPS_PERL_EXECUTABLE}</LocalDebuggerCommand>
|
||||
<LocalDebuggerCommandArguments>slic3r.pl</LocalDebuggerCommandArguments>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
<LocalDebuggerWorkingDirectory>..\..</LocalDebuggerWorkingDirectory>
|
||||
<LocalDebuggerWorkingDirectory>${PROPS_CMAKE_SOURCE_DIR}</LocalDebuggerWorkingDirectory>
|
||||
</PropertyGroup>
|
||||
</Project>
|
@ -1,8 +1,9 @@
|
||||
# Building Slic3r PE on Microsoft Windows
|
||||
|
||||
The currently supported way of building Slic3r PE on Windows is with MS Visual Studio 2013
|
||||
The currently supported way of building Slic3r PE on Windows is with CMake and MS Visual Studio 2013
|
||||
using our Perl binary distribution (compiled from official Perl sources).
|
||||
You can use the free [Visual Studio 2013 Community Edition](https://www.visualstudio.com/vs/older-downloads/).
|
||||
CMake installer can be downloaded from [the official website](https://cmake.org/download/).
|
||||
|
||||
Other setups (such as mingw + Strawberry Perl) _may_ work, but we cannot guarantee this will work
|
||||
and cannot provide guidance.
|
||||
@ -26,8 +27,8 @@ Apart from wxWidgets and Perl, you will also need additional dependencies:
|
||||
|
||||
We have prepared a binary package of the listed libraries:
|
||||
|
||||
- 32 bit: [slic3r-destdir-32.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-32.7z)
|
||||
- 64 bit: [slic3r-destdir-64.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-64.7z)
|
||||
- 32 bit: [slic3r-destdir-32.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=2%2Fslic3r-destdir-32.7z)
|
||||
- 64 bit: [slic3r-destdir-64.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=2%2Fslic3r-destdir-64.7z)
|
||||
|
||||
It is recommended you unpack this package into `C:\local\` as the environment
|
||||
setup script expects it there.
|
||||
|
@ -37,7 +37,7 @@ if ($destdir -eq "") {
|
||||
}
|
||||
|
||||
$BOOST = 'boost_1_63_0'
|
||||
$CURL = 'curl-7.28.0'
|
||||
$CURL = 'curl-7.58.0'
|
||||
$TBB_SHA = 'a0dc9bf76d0120f917b641ed095360448cabc85b'
|
||||
$TBB = "tbb-$TBB_SHA"
|
||||
|
||||
|
52
doc/updating/Updatig.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Slic3r PE 1.40 configuration update
|
||||
|
||||
Slic3r PE 1.40.0 comes with a major re-work of the way configuration presets work.
|
||||
There are three new features:
|
||||
|
||||
+ A two-tier system of presets being divided into _System_ and _User_ groups
|
||||
+ Configuration snapshots
|
||||
+ Configuration updating from the internet
|
||||
|
||||
## System and User presets
|
||||
|
||||
- _System preset_: These are the presets that come with Slic3r PE installation. They come from a vendor configuration bundle (not individual files like before). They are **read-only** – a user cannot modify them, but may instead create a derived User preset based on a System preset
|
||||
- _User preset_: These are regular presets stored in files just like before. Additionally, they may be derived (inherited) from one of the System presets
|
||||
|
||||
A derived User preset keeps track of wich settings are inherited from the parent System preset and which are modified by the user. When a system preset is updated (either via installation of a new Slic3r or automatically from the internet), in a User preset the settings that are modified by the user will stay that way, while the ones that are inherited reflect the updated System preset.
|
||||
|
||||
This system ensures that we don't overwrite user's settings when there is an update to the built in presets.
|
||||
|
||||
Slic3r GUI now displays accurately which settings are inherited and which are modified.
|
||||
A setting derived from a System preset is represented by green label and a locked lock icon:
|
||||
|
||||
![a system setting](setting_sys.png)
|
||||
|
||||
A settings modified in a User preset has an open lock icon:
|
||||
|
||||
![a user setting](setting_user.png)
|
||||
|
||||
Clickign the open lock icon restored the system setting.
|
||||
|
||||
Additionaly, any setting that is modified but not yet saved onto disk is represented by orange label and a back-arrow:
|
||||
|
||||
![a modified setting](setting_mod.png)
|
||||
|
||||
Clicking the back-arrow restores the value that was previously saved in this Preset.
|
||||
|
||||
## Configuration snapshots
|
||||
|
||||
Configuration snapshots can now be taken via the _Configuration_ menu.
|
||||
A snapshot contains complete configuration from the point when the snapshot was taken.
|
||||
Users may move back and forth between snapshots at will using a dialog:
|
||||
|
||||
![snapshots dialog](snapshots_dialog.png)
|
||||
|
||||
|
||||
# Updating from the internet
|
||||
|
||||
Slic3r PE 1.40.0 checks for updates of the built-in System presets and downloads them.
|
||||
The first-time configuration assistant will ask you if you want to enable this feature - it is **not** mandatory.
|
||||
|
||||
Updates are checked for and downloaded in the background. If there's is an update, Slic3r will prompt about it
|
||||
next time it is launched, never during normal program operation. An update may be either accepted or refused.
|
||||
Before any update is applied a configuration snapshot (as described above) is taken.
|
BIN
doc/updating/setting_mod.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
doc/updating/setting_sys.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
doc/updating/setting_user.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
doc/updating/snapshots_dialog.png
Normal file
After Width: | Height: | Size: 77 KiB |
@ -7,12 +7,6 @@ use warnings;
|
||||
use List::Util qw(first);
|
||||
use Slic3r::Geometry::Clipper qw(union_ex diff_pl);
|
||||
|
||||
sub wkt {
|
||||
my $self = shift;
|
||||
return sprintf "POLYGON(%s)",
|
||||
join ',', map "($_)", map { join ',', map "$_->[0] $_->[1]", @$_ } @$self;
|
||||
}
|
||||
|
||||
sub offset {
|
||||
my $self = shift;
|
||||
return Slic3r::Geometry::Clipper::offset(\@$self, @_);
|
||||
|
@ -7,9 +7,6 @@ use File::Basename qw(basename);
|
||||
use FindBin;
|
||||
use List::Util qw(first);
|
||||
use Slic3r::GUI::2DBed;
|
||||
use Slic3r::GUI::AboutDialog;
|
||||
use Slic3r::GUI::BedShapeDialog;
|
||||
use Slic3r::GUI::ConfigWizard;
|
||||
use Slic3r::GUI::Controller;
|
||||
use Slic3r::GUI::Controller::ManualControlDialog;
|
||||
use Slic3r::GUI::Controller::PrinterPanel;
|
||||
@ -70,6 +67,8 @@ our $grey = Wx::Colour->new(200,200,200);
|
||||
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) = @_;
|
||||
@ -86,7 +85,9 @@ sub OnInit {
|
||||
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
|
||||
@ -95,28 +96,27 @@ sub OnInit {
|
||||
warn $@ . "\n";
|
||||
fatal_error(undef, $@);
|
||||
}
|
||||
my $run_wizard = ! $self->{app_config}->exists;
|
||||
my $app_conf_exists = $self->{app_config}->exists;
|
||||
# load settings
|
||||
$self->{app_config}->load if ! $run_wizard;
|
||||
$self->{app_config}->load if $app_conf_exists;
|
||||
$self->{app_config}->set('version', $Slic3r::VERSION);
|
||||
$self->{app_config}->save;
|
||||
|
||||
Slic3r::GUI::set_app_config($self->{app_config});
|
||||
$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 };
|
||||
eval { $self->{preset_bundle}->load_presets($self->{app_config}); };
|
||||
if ($@) {
|
||||
warn $@ . "\n";
|
||||
show_error(undef, $@);
|
||||
}
|
||||
eval { $self->{preset_bundle}->load_selections($self->{app_config}) };
|
||||
$run_wizard = 1 if $self->{preset_bundle}->has_defauls_only;
|
||||
|
||||
Slic3r::GUI::set_preset_bundle($self->{preset_bundle});
|
||||
|
||||
# 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(
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
@ -127,7 +127,7 @@ sub OnInit {
|
||||
);
|
||||
$self->SetTopWindow($frame);
|
||||
|
||||
#EVT_IDLE($frame, sub {
|
||||
# This makes CallAfter() work
|
||||
EVT_IDLE($self->{mainframe}, sub {
|
||||
while (my $cb = shift @cb) {
|
||||
$cb->();
|
||||
@ -135,17 +135,32 @@ sub OnInit {
|
||||
$self->{app_config}->save if $self->{app_config}->dirty;
|
||||
});
|
||||
|
||||
if ($run_wizard) {
|
||||
# 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.
|
||||
$self->{mainframe}->config_wizard(1);
|
||||
});
|
||||
}
|
||||
# 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;
|
||||
});
|
||||
|
||||
@ -154,10 +169,20 @@ sub OnInit {
|
||||
$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(
|
||||
@ -174,22 +199,19 @@ sub recreate_GUI{
|
||||
$topwindow->Destroy;
|
||||
}
|
||||
|
||||
my $run_wizard = 1 if $self->{preset_bundle}->has_defauls_only;
|
||||
if ($run_wizard) {
|
||||
# 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.
|
||||
$self->{mainframe}->config_wizard(1);
|
||||
});
|
||||
}
|
||||
}
|
||||
EVT_IDLE($self->{mainframe}, sub {
|
||||
while (my $cb = shift @cb) {
|
||||
$cb->();
|
||||
}
|
||||
$self->{app_config}->save if $self->{app_config}->dirty;
|
||||
});
|
||||
|
||||
sub about {
|
||||
my ($self) = @_;
|
||||
my $about = Slic3r::GUI::AboutDialog->new(undef);
|
||||
$about->ShowModal;
|
||||
$about->Destroy;
|
||||
# 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 {
|
||||
@ -201,8 +223,8 @@ sub system_info {
|
||||
my $opengl_info_txt = '';
|
||||
if (defined($self->{mainframe}) && defined($self->{mainframe}->{plater}) &&
|
||||
defined($self->{mainframe}->{plater}->{canvas3D})) {
|
||||
$opengl_info = $self->{mainframe}->{plater}->{canvas3D}->opengl_info(format => 'html');
|
||||
$opengl_info_txt = $self->{mainframe}->{plater}->{canvas3D}->opengl_info;
|
||||
$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,
|
||||
@ -232,7 +254,7 @@ sub catch_error {
|
||||
# static method accepting a wxWindow object as first parameter
|
||||
sub show_error {
|
||||
my ($parent, $message) = @_;
|
||||
Wx::MessageDialog->new($parent, $message, 'Error', wxOK | wxICON_ERROR)->ShowModal;
|
||||
Slic3r::GUI::show_error_id($parent ? $parent->GetId() : 0, $message);
|
||||
}
|
||||
|
||||
# static method accepting a wxWindow object as first parameter
|
||||
|
@ -1,4 +1,5 @@
|
||||
# Bed shape dialog
|
||||
# still used by the Slic3r::GUI::Controller::ManualControlDialog Perl module.
|
||||
|
||||
package Slic3r::GUI::2DBed;
|
||||
use strict;
|
||||
|
@ -1,122 +0,0 @@
|
||||
package Slic3r::GUI::AboutDialog;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx qw(:font :html :misc :dialog :sizer :systemsettings :frame :id);
|
||||
use Wx::Event qw(EVT_HTML_LINK_CLICKED EVT_LEFT_DOWN EVT_BUTTON);
|
||||
use Wx::Print;
|
||||
use Wx::Html;
|
||||
use base 'Wx::Dialog';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, 'About Slic3r', wxDefaultPosition, [600, 340], wxCAPTION);
|
||||
|
||||
$self->SetBackgroundColour(Wx::wxWHITE);
|
||||
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$self->SetSizer($hsizer);
|
||||
|
||||
# logo
|
||||
my $logo = Slic3r::GUI::AboutDialog::Logo->new($self, -1, wxDefaultPosition, wxDefaultSize);
|
||||
$logo->SetBackgroundColour(Wx::wxWHITE);
|
||||
$hsizer->Add($logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 30);
|
||||
|
||||
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$hsizer->Add($vsizer, 1, wxEXPAND, 0);
|
||||
|
||||
# title
|
||||
my $title = Wx::StaticText->new($self, -1, $Slic3r::FORK_NAME, wxDefaultPosition, wxDefaultSize);
|
||||
my $title_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
$title_font->SetWeight(wxFONTWEIGHT_BOLD);
|
||||
$title_font->SetFamily(wxFONTFAMILY_ROMAN);
|
||||
$title_font->SetPointSize(24);
|
||||
$title->SetFont($title_font);
|
||||
$vsizer->Add($title, 0, wxALIGN_LEFT | wxTOP, 30);
|
||||
|
||||
# version
|
||||
my $version = Wx::StaticText->new($self, -1, "Version $Slic3r::VERSION", wxDefaultPosition, wxDefaultSize);
|
||||
my $version_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
$version_font->SetPointSize(&Wx::wxMSW ? 9 : 11);
|
||||
$version->SetFont($version_font);
|
||||
$vsizer->Add($version, 0, wxALIGN_LEFT | wxBOTTOM, 10);
|
||||
|
||||
# text
|
||||
my $text =
|
||||
'<html>' .
|
||||
'<body bgcolor="#ffffff" link="#808080">' .
|
||||
'<font color="#808080">' .
|
||||
'Copyright © 2016 Vojtech Bubnik, Prusa Research. <br />' .
|
||||
'Copyright © 2011-2016 Alessandro Ranellucci. <br />' .
|
||||
'<a href="http://slic3r.org/">Slic3r</a> is licensed under the ' .
|
||||
'<a href="http://www.gnu.org/licenses/agpl-3.0.html">GNU Affero General Public License, version 3</a>.' .
|
||||
'<br /><br /><br />' .
|
||||
'Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Y. Sapir, Mike Sheldrake and numerous others. ' .
|
||||
'Manual by Gary Hodgson. Inspired by the RepRap community. <br />' .
|
||||
'Slic3r logo designed by Corey Daniels, <a href="http://www.famfamfam.com/lab/icons/silk/">Silk Icon Set</a> designed by Mark James. ' .
|
||||
'</font>' .
|
||||
'</body>' .
|
||||
'</html>';
|
||||
my $html = Wx::HtmlWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_NEVER);
|
||||
my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
my $size = &Wx::wxMSW ? 8 : 10;
|
||||
$html->SetFonts($font->GetFaceName, $font->GetFaceName, [$size, $size, $size, $size, $size, $size, $size]);
|
||||
$html->SetBorders(2);
|
||||
$html->SetPage($text);
|
||||
$vsizer->Add($html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 20);
|
||||
EVT_HTML_LINK_CLICKED($self, $html, \&link_clicked);
|
||||
|
||||
my $buttons = $self->CreateStdDialogButtonSizer(wxOK);
|
||||
$self->SetEscapeId(wxID_CLOSE);
|
||||
EVT_BUTTON($self, wxID_CLOSE, sub {
|
||||
$self->EndModal(wxID_CLOSE);
|
||||
$self->Close;
|
||||
});
|
||||
$vsizer->Add($buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||
|
||||
EVT_LEFT_DOWN($self, sub { $self->Close });
|
||||
EVT_LEFT_DOWN($logo, sub { $self->Close });
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub link_clicked {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
Wx::LaunchDefaultBrowser($event->GetLinkInfo->GetHref);
|
||||
$event->Skip(0);
|
||||
}
|
||||
|
||||
package Slic3r::GUI::AboutDialog::Logo;
|
||||
use Wx qw(:bitmap :dc);
|
||||
use Wx::Event qw(EVT_PAINT);
|
||||
use base 'Wx::Panel';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
|
||||
$self->{logo} = Wx::Bitmap->new(Slic3r::var("Slic3r_192px.png"), wxBITMAP_TYPE_PNG);
|
||||
$self->SetMinSize(Wx::Size->new($self->{logo}->GetWidth, $self->{logo}->GetHeight));
|
||||
|
||||
EVT_PAINT($self, \&repaint);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub repaint {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
my $dc = Wx::PaintDC->new($self);
|
||||
$dc->SetBackgroundMode(wxTRANSPARENT);
|
||||
|
||||
my $size = $self->GetSize;
|
||||
my $logo_w = $self->{logo}->GetWidth;
|
||||
my $logo_h = $self->{logo}->GetHeight;
|
||||
$dc->DrawBitmap($self->{logo}, ($size->GetWidth - $logo_w) / 2, ($size->GetHeight - $logo_h) / 2, 1);
|
||||
|
||||
$event->Skip;
|
||||
}
|
||||
|
||||
1;
|
@ -1,316 +0,0 @@
|
||||
# The bed shape dialog.
|
||||
# The dialog opens from Print Settins tab -> Bed Shape: Set...
|
||||
|
||||
package Slic3r::GUI::BedShapeDialog;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use List::Util qw(min max);
|
||||
use Slic3r::Geometry qw(X Y unscale);
|
||||
use Wx qw(:dialog :id :misc :sizer :choicebook wxTAB_TRAVERSAL);
|
||||
use Wx::Event qw(EVT_CLOSE);
|
||||
use base 'Wx::Dialog';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $default) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, "Bed Shape", wxDefaultPosition, [350,700], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||
|
||||
$self->{panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $default);
|
||||
|
||||
my $main_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$main_sizer->Add($panel, 1, wxEXPAND);
|
||||
$main_sizer->Add($self->CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND);
|
||||
|
||||
$self->SetSizer($main_sizer);
|
||||
$self->SetMinSize($self->GetSize);
|
||||
$main_sizer->SetSizeHints($self);
|
||||
|
||||
# needed to actually free memory
|
||||
EVT_CLOSE($self, sub {
|
||||
$self->EndModal(wxID_OK);
|
||||
$self->Destroy;
|
||||
});
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub GetValue {
|
||||
my ($self) = @_;
|
||||
return $self->{panel}->GetValue;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::BedShapePanel;
|
||||
|
||||
use List::Util qw(min max sum first);
|
||||
use Scalar::Util qw(looks_like_number);
|
||||
use Slic3r::Geometry qw(PI X Y unscale scaled_epsilon);
|
||||
use Wx qw(:font :id :misc :sizer :choicebook :filedialog :pen :brush wxTAB_TRAVERSAL);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_CHOICEBOOK_PAGE_CHANGED EVT_BUTTON);
|
||||
use base 'Wx::Panel';
|
||||
|
||||
use constant SHAPE_RECTANGULAR => 0;
|
||||
use constant SHAPE_CIRCULAR => 1;
|
||||
use constant SHAPE_CUSTOM => 2;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $default) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1);
|
||||
|
||||
$self->on_change(undef);
|
||||
|
||||
my $box = Wx::StaticBox->new($self, -1, "Shape");
|
||||
my $sbsizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
|
||||
|
||||
# shape options
|
||||
$self->{shape_options_book} = Wx::Choicebook->new($self, -1, wxDefaultPosition, [300,-1], wxCHB_TOP);
|
||||
$sbsizer->Add($self->{shape_options_book});
|
||||
|
||||
$self->{optgroups} = [];
|
||||
{
|
||||
my $optgroup = $self->_init_shape_options_page('Rectangular');
|
||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'rect_size',
|
||||
type => 'point',
|
||||
label => 'Size',
|
||||
tooltip => 'Size in X and Y of the rectangular plate.',
|
||||
default => [200,200],
|
||||
));
|
||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'rect_origin',
|
||||
type => 'point',
|
||||
label => 'Origin',
|
||||
tooltip => 'Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.',
|
||||
default => [0,0],
|
||||
));
|
||||
}
|
||||
{
|
||||
my $optgroup = $self->_init_shape_options_page('Circular');
|
||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'diameter',
|
||||
type => 'f',
|
||||
label => 'Diameter',
|
||||
tooltip => 'Diameter of the print bed. It is assumed that origin (0,0) is located in the center.',
|
||||
sidetext => 'mm',
|
||||
default => 200,
|
||||
));
|
||||
}
|
||||
{
|
||||
my $optgroup = $self->_init_shape_options_page('Custom');
|
||||
$optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new(
|
||||
full_width => 1,
|
||||
widget => sub {
|
||||
my ($parent) = @_;
|
||||
|
||||
my $btn = Wx::Button->new($parent, -1, "Load shape from STL...", wxDefaultPosition, wxDefaultSize);
|
||||
EVT_BUTTON($self, $btn, sub { $self->_load_stl });
|
||||
return $btn;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
EVT_CHOICEBOOK_PAGE_CHANGED($self, -1, sub {
|
||||
$self->_update_shape;
|
||||
});
|
||||
|
||||
# right pane with preview canvas
|
||||
my $canvas = $self->{canvas} = Slic3r::GUI::2DBed->new($self);
|
||||
|
||||
# main sizer
|
||||
my $top_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$top_sizer->Add($sbsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
$top_sizer->Add($canvas, 1, wxEXPAND | wxALL, 10) if $canvas;
|
||||
|
||||
$self->SetSizerAndFit($top_sizer);
|
||||
|
||||
$self->_set_shape($default);
|
||||
$self->_update_preview;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub on_change {
|
||||
my ($self, $cb) = @_;
|
||||
$self->{on_change} = $cb // sub {};
|
||||
}
|
||||
|
||||
# Called from the constructor.
|
||||
# Set the initial bed shape from a list of points.
|
||||
# Deduce the bed shape type (rect, circle, custom)
|
||||
# This routine shall be smart enough if the user messes up
|
||||
# with the list of points in the ini file directly.
|
||||
sub _set_shape {
|
||||
my ($self, $points) = @_;
|
||||
|
||||
# is this a rectangle?
|
||||
if (@$points == 4) {
|
||||
my $polygon = Slic3r::Polygon->new_scale(@$points);
|
||||
my $lines = $polygon->lines;
|
||||
if ($lines->[0]->parallel_to_line($lines->[2]) && $lines->[1]->parallel_to_line($lines->[3])) {
|
||||
# okay, it's a rectangle
|
||||
|
||||
# find origin
|
||||
# the || 0 hack prevents "-0" which might confuse the user
|
||||
my $x_min = min(map $_->[X], @$points) || 0;
|
||||
my $x_max = max(map $_->[X], @$points) || 0;
|
||||
my $y_min = min(map $_->[Y], @$points) || 0;
|
||||
my $y_max = max(map $_->[Y], @$points) || 0;
|
||||
my $origin = [-$x_min, -$y_min];
|
||||
|
||||
$self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR);
|
||||
my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR];
|
||||
$optgroup->set_value('rect_size', [ $x_max-$x_min, $y_max-$y_min ]);
|
||||
$optgroup->set_value('rect_origin', $origin);
|
||||
$self->_update_shape;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# is this a circle?
|
||||
{
|
||||
# Analyze the array of points. Do they reside on a circle?
|
||||
my $polygon = Slic3r::Polygon->new_scale(@$points);
|
||||
my $center = $polygon->bounding_box->center;
|
||||
my @vertex_distances = map $center->distance_to($_), @$polygon;
|
||||
my $avg_dist = sum(@vertex_distances)/@vertex_distances;
|
||||
if (!defined first { abs($_ - $avg_dist) > 10*scaled_epsilon } @vertex_distances) {
|
||||
# all vertices are equidistant to center
|
||||
$self->{shape_options_book}->SetSelection(SHAPE_CIRCULAR);
|
||||
my $optgroup = $self->{optgroups}[SHAPE_CIRCULAR];
|
||||
$optgroup->set_value('diameter', sprintf("%.0f", unscale($avg_dist*2)));
|
||||
$self->_update_shape;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (@$points < 3) {
|
||||
# Invalid polygon. Revert to default bed dimensions.
|
||||
$self->{shape_options_book}->SetSelection(SHAPE_RECTANGULAR);
|
||||
my $optgroup = $self->{optgroups}[SHAPE_RECTANGULAR];
|
||||
$optgroup->set_value('rect_size', [200, 200]);
|
||||
$optgroup->set_value('rect_origin', [0, 0]);
|
||||
$self->_update_shape;
|
||||
return;
|
||||
}
|
||||
|
||||
# This is a custom bed shape, use the polygon provided.
|
||||
$self->{shape_options_book}->SetSelection(SHAPE_CUSTOM);
|
||||
# Copy the polygon to the canvas, make a copy of the array.
|
||||
$self->{canvas}->bed_shape([@$points]);
|
||||
$self->_update_shape;
|
||||
}
|
||||
|
||||
# Update the bed shape from the dialog fields.
|
||||
sub _update_shape {
|
||||
my ($self) = @_;
|
||||
|
||||
my $page_idx = $self->{shape_options_book}->GetSelection;
|
||||
if ($page_idx == SHAPE_RECTANGULAR) {
|
||||
my $rect_size = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_size');
|
||||
my $rect_origin = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_origin');
|
||||
my ($x, $y) = @$rect_size;
|
||||
return if !looks_like_number($x) || !looks_like_number($y); # empty strings or '-' or other things
|
||||
return if !$x || !$y or $x == 0 or $y == 0;
|
||||
my ($x0, $y0) = (0,0);
|
||||
my ($x1, $y1) = ($x ,$y);
|
||||
{
|
||||
my ($dx, $dy) = @$rect_origin;
|
||||
return if !looks_like_number($dx) || !looks_like_number($dy); # empty strings or '-' or other things
|
||||
$x0 -= $dx;
|
||||
$x1 -= $dx;
|
||||
$y0 -= $dy;
|
||||
$y1 -= $dy;
|
||||
}
|
||||
$self->{canvas}->bed_shape([
|
||||
[$x0,$y0],
|
||||
[$x1,$y0],
|
||||
[$x1,$y1],
|
||||
[$x0,$y1],
|
||||
]);
|
||||
} elsif ($page_idx == SHAPE_CIRCULAR) {
|
||||
my $diameter = $self->{optgroups}[SHAPE_CIRCULAR]->get_value('diameter');
|
||||
return if !$diameter or $diameter == 0;
|
||||
my $r = $diameter/2;
|
||||
my $twopi = 2*PI;
|
||||
my $edges = 60;
|
||||
my $polygon = Slic3r::Polygon->new_scale(
|
||||
map [ $r * cos $_, $r * sin $_ ],
|
||||
map { $twopi/$edges*$_ } 1..$edges
|
||||
);
|
||||
$self->{canvas}->bed_shape([
|
||||
map [ unscale($_->x), unscale($_->y) ], @$polygon #))
|
||||
]);
|
||||
}
|
||||
|
||||
$self->{on_change}->();
|
||||
$self->_update_preview;
|
||||
}
|
||||
|
||||
sub _update_preview {
|
||||
my ($self) = @_;
|
||||
$self->{canvas}->Refresh if $self->{canvas};
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
# Called from the constructor.
|
||||
# Create a panel for a rectangular / circular / custom bed shape.
|
||||
sub _init_shape_options_page {
|
||||
my ($self, $title) = @_;
|
||||
|
||||
my $panel = Wx::Panel->new($self->{shape_options_book});
|
||||
my $optgroup;
|
||||
push @{$self->{optgroups}}, $optgroup = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $panel,
|
||||
title => 'Settings',
|
||||
label_width => 100,
|
||||
on_change => sub {
|
||||
my ($opt_id) = @_;
|
||||
#$self->{"_$opt_id"} = $optgroup->get_value($opt_id);
|
||||
$self->_update_shape;
|
||||
},
|
||||
);
|
||||
$panel->SetSizerAndFit($optgroup->sizer);
|
||||
$self->{shape_options_book}->AddPage($panel, $title);
|
||||
|
||||
return $optgroup;
|
||||
}
|
||||
|
||||
# Loads an stl file, projects it to the XY plane and calculates a polygon.
|
||||
sub _load_stl {
|
||||
my ($self) = @_;
|
||||
|
||||
my $dialog = Wx::FileDialog->new($self, 'Choose a file to import bed shape from (STL/OBJ/AMF/PRUSA):', "", "", &Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if ($dialog->ShowModal != wxID_OK) {
|
||||
$dialog->Destroy;
|
||||
return;
|
||||
}
|
||||
my $input_file = $dialog->GetPaths;
|
||||
$dialog->Destroy;
|
||||
|
||||
my $model = Slic3r::Model->read_from_file($input_file);
|
||||
my $mesh = $model->mesh;
|
||||
my $expolygons = $mesh->horizontal_projection;
|
||||
|
||||
if (@$expolygons == 0) {
|
||||
Slic3r::GUI::show_error($self, "The selected file contains no geometry.");
|
||||
return;
|
||||
}
|
||||
if (@$expolygons > 1) {
|
||||
Slic3r::GUI::show_error($self, "The selected file contains several disjoint areas. This is not supported.");
|
||||
return;
|
||||
}
|
||||
|
||||
my $polygon = $expolygons->[0]->contour;
|
||||
$self->{canvas}->bed_shape([ map [ unscale($_->x), unscale($_->y) ], @$polygon ]);
|
||||
$self->_update_preview();
|
||||
}
|
||||
|
||||
# Returns the resulting bed shape polygon. This value will be stored to the ini file.
|
||||
sub GetValue {
|
||||
my ($self) = @_;
|
||||
return $self->{canvas}->bed_shape;
|
||||
}
|
||||
|
||||
1;
|
@ -1,458 +0,0 @@
|
||||
# The config wizard is executed when the Slic3r is first started.
|
||||
# The wizard helps the user to specify the 3D printer properties.
|
||||
|
||||
package Slic3r::GUI::ConfigWizard;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx;
|
||||
use base 'Wx::Wizard';
|
||||
|
||||
# adhere to various human interface guidelines
|
||||
our $wizard = 'Wizard';
|
||||
$wizard = 'Assistant' if &Wx::wxMAC || &Wx::wxGTK;
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, $presets, $fresh_start) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, "Configuration $wizard");
|
||||
|
||||
# initialize an empty repository
|
||||
$self->{config} = Slic3r::Config->new;
|
||||
|
||||
my $welcome_page = Slic3r::GUI::ConfigWizard::Page::Welcome->new($self, $fresh_start);
|
||||
$self->add_page($welcome_page);
|
||||
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Firmware->new($self));
|
||||
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Bed->new($self));
|
||||
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Nozzle->new($self));
|
||||
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Filament->new($self));
|
||||
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Temperature->new($self));
|
||||
$self->add_page(Slic3r::GUI::ConfigWizard::Page::BedTemperature->new($self));
|
||||
$self->add_page(Slic3r::GUI::ConfigWizard::Page::Finished->new($self));
|
||||
|
||||
$_->build_index for @{$self->{pages}};
|
||||
|
||||
$welcome_page->set_selection_presets([@{$presets}, 'Other']);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub add_page {
|
||||
my ($self, $page) = @_;
|
||||
|
||||
my $n = push @{$self->{pages}}, $page;
|
||||
# add first page to the page area sizer
|
||||
$self->GetPageAreaSizer->Add($page) if $n == 1;
|
||||
# link pages
|
||||
$self->{pages}[$n-2]->set_next_page($page) if $n >= 2;
|
||||
$page->set_previous_page($self->{pages}[$n-2]) if $n >= 2;
|
||||
}
|
||||
|
||||
sub run {
|
||||
my ($self) = @_;
|
||||
my $result;
|
||||
if (Wx::Wizard::RunWizard($self, $self->{pages}[0])) {
|
||||
my $preset_name = $self->{pages}[0]->{preset_name};
|
||||
$result = {
|
||||
preset_name => $preset_name,
|
||||
reset_user_profile => $self->{pages}[0]->{reset_user_profile}
|
||||
};
|
||||
if ($preset_name eq 'Other') {
|
||||
# it would be cleaner to have these defined inside each page class,
|
||||
# in some event getting called before leaving the page
|
||||
# set first_layer_height + layer_height based on nozzle_diameter
|
||||
my $nozzle = $self->{config}->nozzle_diameter;
|
||||
$self->{config}->set('first_layer_height', $nozzle->[0]);
|
||||
$self->{config}->set('layer_height', $nozzle->[0] - 0.1);
|
||||
|
||||
# set first_layer_temperature to temperature + 5
|
||||
$self->{config}->set('first_layer_temperature', [$self->{config}->temperature->[0] + 5]);
|
||||
|
||||
# set first_layer_bed_temperature to temperature + 5
|
||||
$self->{config}->set('first_layer_bed_temperature',
|
||||
[ ($self->{config}->bed_temperature->[0] > 0) ? ($self->{config}->bed_temperature->[0] + 5) : 0 ]);
|
||||
$result->{config} = $self->{config};
|
||||
}
|
||||
}
|
||||
$self->Destroy;
|
||||
return $result;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::ConfigWizard::Index;
|
||||
use Wx qw(:bitmap :dc :font :misc :sizer :systemsettings :window);
|
||||
use Wx::Event qw(EVT_ERASE_BACKGROUND EVT_PAINT);
|
||||
use base 'Wx::Panel';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $title) = @_;
|
||||
my $self = $class->SUPER::new($parent);
|
||||
|
||||
push @{$self->{titles}}, $title;
|
||||
$self->{own_index} = 0;
|
||||
|
||||
$self->{bullets}->{before} = Wx::Bitmap->new(Slic3r::var("bullet_black.png"), wxBITMAP_TYPE_PNG);
|
||||
$self->{bullets}->{own} = Wx::Bitmap->new(Slic3r::var("bullet_blue.png"), wxBITMAP_TYPE_PNG);
|
||||
$self->{bullets}->{after} = Wx::Bitmap->new(Slic3r::var("bullet_white.png"), wxBITMAP_TYPE_PNG);
|
||||
|
||||
$self->{background} = Wx::Bitmap->new(Slic3r::var("Slic3r_192px_transparent.png"), wxBITMAP_TYPE_PNG);
|
||||
$self->SetMinSize(Wx::Size->new($self->{background}->GetWidth, $self->{background}->GetHeight));
|
||||
|
||||
EVT_PAINT($self, \&repaint);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub repaint {
|
||||
my ($self, $event) = @_;
|
||||
my $size = $self->GetClientSize;
|
||||
my $gap = 5;
|
||||
|
||||
my $dc = Wx::PaintDC->new($self);
|
||||
$dc->SetBackgroundMode(wxTRANSPARENT);
|
||||
$dc->SetFont($self->GetFont);
|
||||
$dc->SetTextForeground($self->GetForegroundColour);
|
||||
|
||||
my $background_h = $self->{background}->GetHeight;
|
||||
my $background_w = $self->{background}->GetWidth;
|
||||
$dc->DrawBitmap($self->{background}, ($size->GetWidth - $background_w) / 2, ($size->GetHeight - $background_h) / 2, 1);
|
||||
|
||||
my $label_h = $self->{bullets}->{own}->GetHeight;
|
||||
$label_h = $dc->GetCharHeight if $dc->GetCharHeight > $label_h;
|
||||
my $label_w = $size->GetWidth;
|
||||
|
||||
my $i = 0;
|
||||
foreach (@{$self->{titles}}) {
|
||||
my $bullet = $self->{bullets}->{own};
|
||||
$bullet = $self->{bullets}->{before} if $i < $self->{own_index};
|
||||
$bullet = $self->{bullets}->{after} if $i > $self->{own_index};
|
||||
|
||||
$dc->SetTextForeground(Wx::Colour->new(128, 128, 128)) if $i > $self->{own_index};
|
||||
$dc->DrawLabel($_, $bullet, Wx::Rect->new(0, $i * ($label_h + $gap), $label_w, $label_h));
|
||||
# Only show the first bullet if this is the only wizard page to be displayed.
|
||||
last if $i == 0 && $self->{just_welcome};
|
||||
$i++;
|
||||
}
|
||||
|
||||
$event->Skip;
|
||||
}
|
||||
|
||||
sub prepend_title {
|
||||
my $self = shift;
|
||||
my ($title) = @_;
|
||||
|
||||
unshift @{$self->{titles}}, $title;
|
||||
$self->{own_index}++;
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
sub append_title {
|
||||
my $self = shift;
|
||||
my ($title) = @_;
|
||||
|
||||
push @{$self->{titles}}, $title;
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::ConfigWizard::Page;
|
||||
use Wx qw(:font :misc :sizer :staticline :systemsettings);
|
||||
use base 'Wx::WizardPage';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $title, $short_title) = @_;
|
||||
my $self = $class->SUPER::new($parent);
|
||||
|
||||
my $sizer = Wx::FlexGridSizer->new(0, 2, 10, 10);
|
||||
$sizer->AddGrowableCol(1, 1);
|
||||
$sizer->AddGrowableRow(1, 1);
|
||||
$sizer->AddStretchSpacer(0);
|
||||
$self->SetSizer($sizer);
|
||||
|
||||
# title
|
||||
my $text = Wx::StaticText->new($self, -1, $title, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
my $bold_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
$bold_font->SetWeight(wxFONTWEIGHT_BOLD);
|
||||
$bold_font->SetPointSize(14);
|
||||
$text->SetFont($bold_font);
|
||||
$sizer->Add($text, 0, wxALIGN_LEFT, 0);
|
||||
|
||||
# index
|
||||
$self->{short_title} = $short_title ? $short_title : $title;
|
||||
$self->{index} = Slic3r::GUI::ConfigWizard::Index->new($self, $self->{short_title});
|
||||
$sizer->Add($self->{index}, 1, wxEXPAND | wxTOP | wxRIGHT, 10);
|
||||
|
||||
# contents
|
||||
$self->{width} = 430;
|
||||
$self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$sizer->Add($self->{vsizer}, 1);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub append_text {
|
||||
my $self = shift;
|
||||
my ($text) = @_;
|
||||
|
||||
my $para = Wx::StaticText->new($self, -1, $text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
|
||||
$para->Wrap($self->{width});
|
||||
$para->SetMinSize([$self->{width}, -1]);
|
||||
$self->{vsizer}->Add($para, 0, wxALIGN_LEFT | wxTOP | wxBOTTOM, 10);
|
||||
}
|
||||
|
||||
sub append_option {
|
||||
my $self = shift;
|
||||
my ($full_key) = @_;
|
||||
|
||||
# populate repository with the factory default
|
||||
my ($opt_key, $opt_index) = split /#/, $full_key, 2;
|
||||
$self->config->apply(Slic3r::Config::new_from_defaults_keys([$opt_key]));
|
||||
|
||||
# draw the control
|
||||
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
|
||||
parent => $self,
|
||||
title => '',
|
||||
config => $self->config,
|
||||
full_labels => 1,
|
||||
);
|
||||
$optgroup->append_single_option_line($opt_key, $opt_index);
|
||||
$self->{vsizer}->Add($optgroup->sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
}
|
||||
|
||||
sub append_panel {
|
||||
my ($self, $panel) = @_;
|
||||
$self->{vsizer}->Add($panel, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
}
|
||||
|
||||
sub set_previous_page {
|
||||
my $self = shift;
|
||||
my ($previous_page) = @_;
|
||||
$self->{previous_page} = $previous_page;
|
||||
}
|
||||
|
||||
sub GetPrev {
|
||||
my $self = shift;
|
||||
return $self->{previous_page};
|
||||
}
|
||||
|
||||
sub set_next_page {
|
||||
my $self = shift;
|
||||
my ($next_page) = @_;
|
||||
$self->{next_page} = $next_page;
|
||||
}
|
||||
|
||||
sub GetNext {
|
||||
my $self = shift;
|
||||
return $self->{next_page};
|
||||
}
|
||||
|
||||
sub get_short_title {
|
||||
my $self = shift;
|
||||
return $self->{short_title};
|
||||
}
|
||||
|
||||
sub build_index {
|
||||
my $self = shift;
|
||||
|
||||
my $page = $self;
|
||||
$self->{index}->prepend_title($page->get_short_title) while ($page = $page->GetPrev);
|
||||
$page = $self;
|
||||
$self->{index}->append_title($page->get_short_title) while ($page = $page->GetNext);
|
||||
}
|
||||
|
||||
sub config {
|
||||
my ($self) = @_;
|
||||
return $self->GetParent->{config};
|
||||
}
|
||||
|
||||
package Slic3r::GUI::ConfigWizard::Page::Welcome;
|
||||
use base 'Slic3r::GUI::ConfigWizard::Page';
|
||||
use Wx qw(:misc :sizer wxID_FORWARD);
|
||||
use Wx::Event qw(EVT_ACTIVATE EVT_CHOICE EVT_CHECKBOX);
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, $fresh_start) = @_;
|
||||
my $self = $class->SUPER::new($parent, "Welcome to the Slic3r Configuration $wizard", 'Welcome');
|
||||
$self->{full_wizard_workflow} = 1;
|
||||
$self->{reset_user_profile} = 0;
|
||||
|
||||
# Test for the existence of the old config path.
|
||||
my $message_has_legacy;
|
||||
{
|
||||
my $datadir = Slic3r::data_dir;
|
||||
if ($datadir =~ /Slic3rPE/) {
|
||||
# Check for existence of the legacy Slic3r directory.
|
||||
my $datadir_legacy = substr $datadir, 0, -2;
|
||||
my $dir_enc = Slic3r::encode_path($datadir_legacy);
|
||||
if (-e $dir_enc && -d $dir_enc &&
|
||||
-e ($dir_enc . '/print') && -d ($dir_enc . '/print') &&
|
||||
-e ($dir_enc . '/filament') && -d ($dir_enc . '/filament') &&
|
||||
-e ($dir_enc . '/printer') && -d ($dir_enc . '/printer') &&
|
||||
-e ($dir_enc . '/slic3r.ini')) {
|
||||
$message_has_legacy = "Starting with Slic3r 1.38.4, the user profile directory has been renamed to $datadir. You may consider closing Slic3r and renaming $datadir_legacy to $datadir.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$self->append_text('Hello, welcome to Slic3r Prusa Edition! This '.lc($wizard).' helps you with the initial configuration; just a few settings and you will be ready to print.');
|
||||
$self->append_text('Please select your printer vendor and printer type. If your printer is not listed, you may try your luck and select a similar one. If you select "Other", this ' . lc($wizard) . ' will let you set the basic 3D printer parameters.');
|
||||
$self->append_text($message_has_legacy) if defined $message_has_legacy;
|
||||
# To import an existing configuration instead, cancel this '.lc($wizard).' and use the Open Config menu item found in the File menu.');
|
||||
$self->append_text('If you received a configuration file or a config bundle from your 3D printer vendor, cancel this '.lc($wizard).' and use the "File->Load Config" or "File->Load Config Bundle" menu.');
|
||||
|
||||
$self->{choice} = my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, wxDefaultSize, []);
|
||||
$self->{vsizer}->Add($choice, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
if (! $fresh_start) {
|
||||
$self->{reset_checkbox} = Wx::CheckBox->new($self, -1, "Reset user profile, install from scratch");
|
||||
$self->{vsizer}->Add($self->{reset_checkbox}, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
}
|
||||
|
||||
EVT_CHOICE($parent, $choice, sub {
|
||||
my $sel = $self->{choice}->GetStringSelection;
|
||||
$self->{preset_name} = $sel;
|
||||
$self->set_full_wizard_workflow(($sel eq 'Other') || ($sel eq ''));
|
||||
});
|
||||
|
||||
if (! $fresh_start) {
|
||||
EVT_CHECKBOX($self, $self->{reset_checkbox}, sub {
|
||||
$self->{reset_user_profile} = $self->{reset_checkbox}->GetValue();
|
||||
});
|
||||
}
|
||||
|
||||
EVT_ACTIVATE($parent, sub {
|
||||
$self->set_full_wizard_workflow($self->{preset_name} eq 'Other');
|
||||
});
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub set_full_wizard_workflow {
|
||||
my ($self, $full_workflow) = @_;
|
||||
$self->{full_wizard_workflow} = $full_workflow;
|
||||
$self->{index}->{just_welcome} = !$full_workflow;
|
||||
$self->{index}->Refresh;
|
||||
my $next_button = $self->GetParent->FindWindow(wxID_FORWARD);
|
||||
$next_button->SetLabel($full_workflow ? "&Next >" : "&Finish");
|
||||
}
|
||||
|
||||
# Set the preset names, select the first item.
|
||||
sub set_selection_presets {
|
||||
my ($self, $names) = @_;
|
||||
$self->{choice}->Append($names);
|
||||
$self->{choice}->SetSelection(0);
|
||||
$self->{preset_name} = $names->[0];
|
||||
}
|
||||
|
||||
sub GetNext {
|
||||
my $self = shift;
|
||||
return $self->{full_wizard_workflow} ? $self->{next_page} : undef;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::ConfigWizard::Page::Firmware;
|
||||
use base 'Slic3r::GUI::ConfigWizard::Page';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, 'Firmware Type');
|
||||
|
||||
$self->append_text('Choose the type of firmware used by your printer, then click Next.');
|
||||
$self->append_option('gcode_flavor');
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::ConfigWizard::Page::Bed;
|
||||
use base 'Slic3r::GUI::ConfigWizard::Page';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, 'Bed Size');
|
||||
|
||||
$self->append_text('Set the shape of your printer\'s bed, then click Next.');
|
||||
|
||||
$self->config->apply(Slic3r::Config::new_from_defaults_keys(['bed_shape']));
|
||||
$self->{bed_shape_panel} = my $panel = Slic3r::GUI::BedShapePanel->new($self, $self->config->bed_shape);
|
||||
$self->{bed_shape_panel}->on_change(sub {
|
||||
$self->config->set('bed_shape', $self->{bed_shape_panel}->GetValue);
|
||||
});
|
||||
$self->append_panel($self->{bed_shape_panel});
|
||||
return $self;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::ConfigWizard::Page::Nozzle;
|
||||
use base 'Slic3r::GUI::ConfigWizard::Page';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, 'Nozzle Diameter');
|
||||
|
||||
$self->append_text('Enter the diameter of your printer\'s hot end nozzle, then click Next.');
|
||||
$self->append_option('nozzle_diameter#0');
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::ConfigWizard::Page::Filament;
|
||||
use base 'Slic3r::GUI::ConfigWizard::Page';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, 'Filament Diameter');
|
||||
|
||||
$self->append_text('Enter the diameter of your filament, then click Next.');
|
||||
$self->append_text('Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average.');
|
||||
$self->append_option('filament_diameter#0');
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::ConfigWizard::Page::Temperature;
|
||||
use base 'Slic3r::GUI::ConfigWizard::Page';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, 'Extrusion Temperature');
|
||||
|
||||
$self->append_text('Enter the temperature needed for extruding your filament, then click Next.');
|
||||
$self->append_text('A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS.');
|
||||
$self->append_option('temperature#0');
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::ConfigWizard::Page::BedTemperature;
|
||||
use base 'Slic3r::GUI::ConfigWizard::Page';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, 'Bed Temperature');
|
||||
|
||||
$self->append_text('Enter the bed temperature needed for getting your filament to stick to your heated bed, then click Next.');
|
||||
$self->append_text('A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed.');
|
||||
$self->append_option('bed_temperature#0');
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::ConfigWizard::Page::Finished;
|
||||
use base 'Slic3r::GUI::ConfigWizard::Page';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, 'Congratulations!', 'Finish');
|
||||
|
||||
$self->append_text("You have successfully completed the Slic3r Configuration $wizard. " .
|
||||
'Slic3r is now configured for your printer and filament.');
|
||||
$self->append_text('To close this '.lc($wizard).' and apply the newly created configuration, click Finish.');
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
1;
|
@ -7,7 +7,7 @@ use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog);
|
||||
use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog wxBORDER_NONE);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU);
|
||||
use base qw(Wx::ScrolledWindow Class::Accessor);
|
||||
use List::Util qw(first);
|
||||
@ -34,7 +34,7 @@ sub new {
|
||||
# button for adding new printer panels
|
||||
{
|
||||
my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG),
|
||||
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
|
||||
wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||
$btn->SetToolTipString("Add printer…")
|
||||
if $btn->can('SetToolTipString');
|
||||
|
||||
|
@ -9,7 +9,7 @@ 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
|
||||
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';
|
||||
@ -19,6 +19,7 @@ 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.
|
||||
@ -29,12 +30,23 @@ our $PRESETS_CHANGED_EVENT = Wx::NewEventType;
|
||||
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);
|
||||
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';
|
||||
@ -43,7 +55,7 @@ sub new {
|
||||
} else {
|
||||
$self->SetIcon(Wx::Icon->new(Slic3r::var("Slic3r_128px.png"), wxBITMAP_TYPE_PNG));
|
||||
}
|
||||
|
||||
|
||||
# store input params
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
$self->{no_controller} = $params{no_controller};
|
||||
@ -52,11 +64,6 @@ sub new {
|
||||
$self->{lang_ch_event} = $params{lang_ch_event};
|
||||
$self->{preferences_event} = $params{preferences_event};
|
||||
|
||||
# initialize status bar
|
||||
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1);
|
||||
$self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
|
||||
$self->SetStatusBar($self->{statusbar});
|
||||
|
||||
# initialize tabpanel and menubar
|
||||
$self->_init_tabpanel;
|
||||
$self->_init_menubar;
|
||||
@ -65,9 +72,21 @@ sub new {
|
||||
# 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) };
|
||||
|
||||
$self->{loaded} = 1;
|
||||
|
||||
# initialize status bar
|
||||
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new();
|
||||
$self->{statusbar}->Embed;
|
||||
$self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
|
||||
# Make the global status bar and its progress indicator available in C++
|
||||
$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);
|
||||
@ -85,7 +104,7 @@ sub new {
|
||||
# declare events
|
||||
EVT_CLOSE($self, sub {
|
||||
my (undef, $event) = @_;
|
||||
if ($event->CanVeto && !$self->check_unsaved_changes) {
|
||||
if ($event->CanVeto && !Slic3r::GUI::check_unsaved_changes) {
|
||||
$event->Veto;
|
||||
return;
|
||||
}
|
||||
@ -94,12 +113,17 @@ sub new {
|
||||
# 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->{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;
|
||||
}
|
||||
|
||||
@ -112,10 +136,19 @@ sub _init_tabpanel {
|
||||
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), L("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"));
|
||||
if (!$self->{no_controller}) {
|
||||
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), L("Controller"));
|
||||
}
|
||||
@ -136,6 +169,10 @@ sub _init_tabpanel {
|
||||
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};
|
||||
@ -148,14 +185,15 @@ sub _init_tabpanel {
|
||||
|
||||
my $tab = Slic3r::GUI::get_preset_tab($tab_name);
|
||||
if ($self->{plater}) {
|
||||
# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
|
||||
# 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)) {
|
||||
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})))
|
||||
@ -169,9 +207,43 @@ sub _init_tabpanel {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
# 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);
|
||||
});
|
||||
|
||||
# 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($self->{no_controller}, $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT);
|
||||
$self->{options_tabs} = {};
|
||||
for my $tab_name (qw(print filament printer)) {
|
||||
for my $tab_name (qw(print filament sla_material printer)) {
|
||||
$self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name");
|
||||
}
|
||||
|
||||
@ -201,8 +273,14 @@ sub _init_tabpanel {
|
||||
# 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.
|
||||
$self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter}));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,7 +290,7 @@ sub _init_menubar {
|
||||
# File menu
|
||||
my $fileMenu = Wx::Menu->new;
|
||||
{
|
||||
wxTheApp->append_menu_item($fileMenu, L("Open STL/OBJ/AMF…\tCtrl+O"), L('Open a model'), sub {
|
||||
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 {
|
||||
@ -251,6 +329,9 @@ sub _init_menubar {
|
||||
$self->_append_menu_item($fileMenu, L("Slice to SV&G…\tCtrl+G"), L('Slice file to a multi-layer SVG'), sub {
|
||||
$self->quick_slice(save_as => 1, export_svg => 1);
|
||||
}, undef, 'shape_handles.png');
|
||||
$self->_append_menu_item($fileMenu, L("Slice to PNG…"), L('Slice file to a set of PNG files'), sub {
|
||||
$self->slice_to_png; #$self->quick_slice(save_as => 0, export_png => 1);
|
||||
}, 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');
|
||||
@ -259,12 +340,6 @@ sub _init_menubar {
|
||||
$self->repair_stl;
|
||||
}, undef, 'wrench.png');
|
||||
$fileMenu->AppendSeparator();
|
||||
# Cmd+, is standard on OS X - what about other operating systems?
|
||||
$self->_append_menu_item($fileMenu, L("Preferences…\tCtrl+,"), L('Application preferences'), sub {
|
||||
# Opening the C++ preferences dialog.
|
||||
Slic3r::GUI::open_preferences_dialog($self->{preferences_event});
|
||||
}, wxID_PREFERENCES);
|
||||
$fileMenu->AppendSeparator();
|
||||
$self->_append_menu_item($fileMenu, L("&Quit"), L('Quit Slic3r'), sub {
|
||||
$self->Close(0);
|
||||
}, wxID_EXIT);
|
||||
@ -341,11 +416,6 @@ sub _init_menubar {
|
||||
# Help menu
|
||||
my $helpMenu = Wx::Menu->new;
|
||||
{
|
||||
$self->_append_menu_item($helpMenu, L("&Configuration ").$Slic3r::GUI::ConfigWizard::wizard."…", L("Run Configuration ").$Slic3r::GUI::ConfigWizard::wizard, sub {
|
||||
# Run the config wizard, offer the "reset user profile" checkbox.
|
||||
$self->config_wizard(0);
|
||||
});
|
||||
$helpMenu->AppendSeparator();
|
||||
$self->_append_menu_item($helpMenu, L("Prusa 3D Drivers"), L('Open the Prusa3D drivers download page in your browser'), sub {
|
||||
Wx::LaunchDefaultBrowser('http://www.prusa3d.com/drivers/');
|
||||
});
|
||||
@ -366,11 +436,14 @@ sub _init_menubar {
|
||||
$self->_append_menu_item($helpMenu, L("System Info"), L('Show system information'), sub {
|
||||
wxTheApp->system_info;
|
||||
});
|
||||
$self->_append_menu_item($helpMenu, L("Show &Configuration Folder"), L('Show user configuration folder (datadir)'), sub {
|
||||
Slic3r::GUI::desktop_open_datadir_folder();
|
||||
});
|
||||
$self->_append_menu_item($helpMenu, L("Report an Issue"), L('Report an issue on the Slic3r Prusa Edition'), sub {
|
||||
Wx::LaunchDefaultBrowser('http://github.com/prusa3d/slic3r/issues/new');
|
||||
});
|
||||
$self->_append_menu_item($helpMenu, L("&About Slic3r"), L('Show about dialog'), sub {
|
||||
wxTheApp->about;
|
||||
Slic3r::GUI::about;
|
||||
});
|
||||
}
|
||||
|
||||
@ -384,11 +457,9 @@ sub _init_menubar {
|
||||
$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 an optional debug menu
|
||||
# (Select application language from the list of installed languages)
|
||||
Slic3r::GUI::add_debug_menu($menubar, $self->{lang_ch_event});
|
||||
# Add additional menus from C++
|
||||
Slic3r::GUI::add_menus($menubar, $self->{preferences_event}, $self->{lang_ch_event});
|
||||
$menubar->Append($helpMenu, L("&Help"));
|
||||
# Add an optional debug menu. In production code, the add_debug_menu() call should do nothing.
|
||||
$self->SetMenuBar($menubar);
|
||||
}
|
||||
}
|
||||
@ -406,10 +477,18 @@ sub on_plater_selection_changed {
|
||||
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();
|
||||
}
|
||||
|
||||
# To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
|
||||
sub quick_slice {
|
||||
my ($self, %params) = @_;
|
||||
|
||||
my $progress_dialog;
|
||||
eval {
|
||||
# validate configuration
|
||||
my $config = wxTheApp->{preset_bundle}->full_config();
|
||||
@ -482,12 +561,25 @@ sub quick_slice {
|
||||
$qs_last_output_file = $output_file unless $params{export_svg};
|
||||
wxTheApp->{app_config}->update_last_output_dir(dirname($output_file));
|
||||
$dlg->Destroy;
|
||||
} elsif($params{export_png}) {
|
||||
$output_file = $sprint->output_filepath;
|
||||
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.zip/;
|
||||
# my $dlg = Wx::DirDialog->new($self, L('Choose output directory'));
|
||||
my $dlg = Wx::FileDialog->new($self, L('Save zip file as:'),
|
||||
wxTheApp->{app_config}->get_last_output_dir(dirname($output_file)),
|
||||
basename($output_file), '*.zip', wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
if ($dlg->ShowModal != wxID_OK) {
|
||||
$dlg->Destroy;
|
||||
return;
|
||||
}
|
||||
$output_file = $dlg->GetPath;
|
||||
$dlg->Destroy;
|
||||
}
|
||||
|
||||
# show processbar dialog
|
||||
$self->{progress_dialog} = Wx::ProgressDialog->new(L('Slicing…'), L("Processing ").$input_file_basename."…",
|
||||
100, $self, 0);
|
||||
$self->{progress_dialog}->Pulse;
|
||||
$progress_dialog = Wx::ProgressDialog->new(L('Slicing…'), L("Processing ").$input_file_basename."…",
|
||||
100, $self, 4);
|
||||
$progress_dialog->Pulse;
|
||||
|
||||
{
|
||||
my @warnings = ();
|
||||
@ -496,7 +588,11 @@ sub quick_slice {
|
||||
$sprint->output_file($output_file);
|
||||
if ($params{export_svg}) {
|
||||
$sprint->export_svg;
|
||||
} else {
|
||||
}
|
||||
elsif($params{export_png}) {
|
||||
$sprint->export_png;
|
||||
}
|
||||
else {
|
||||
$sprint->export_gcode;
|
||||
}
|
||||
Slic3r::GUI::warning_catcher($self)->($_) for @warnings;
|
||||
@ -578,7 +674,7 @@ sub export_config {
|
||||
sub load_config_file {
|
||||
my ($self, $file) = @_;
|
||||
if (!$file) {
|
||||
return unless $self->check_unsaved_changes;
|
||||
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",
|
||||
@ -597,7 +693,7 @@ sub load_config_file {
|
||||
|
||||
sub export_configbundle {
|
||||
my ($self) = @_;
|
||||
return unless $self->check_unsaved_changes;
|
||||
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;
|
||||
@ -621,7 +717,7 @@ sub export_configbundle {
|
||||
# but that behavior was not documented and likely buggy.
|
||||
sub load_configbundle {
|
||||
my ($self, $file, $reset_user_profile) = @_;
|
||||
return unless $self->check_unsaved_changes;
|
||||
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,
|
||||
@ -655,64 +751,6 @@ sub load_config {
|
||||
$self->{plater}->on_config_change($config) if $self->{plater};
|
||||
}
|
||||
|
||||
sub config_wizard {
|
||||
my ($self, $fresh_start) = @_;
|
||||
# Exit wizard if there are unsaved changes and the user cancels the action.
|
||||
return unless $self->check_unsaved_changes;
|
||||
# Enumerate the profiles bundled with the Slic3r installation under resources/profiles.
|
||||
my $directory = Slic3r::resources_dir() . "/profiles";
|
||||
my @profiles = ();
|
||||
if (opendir(DIR, Slic3r::encode_path($directory))) {
|
||||
while (my $file = readdir(DIR)) {
|
||||
if ($file =~ /\.ini$/) {
|
||||
$file =~ s/\.ini$//;
|
||||
push @profiles, Slic3r::decode_path($file);
|
||||
}
|
||||
}
|
||||
closedir(DIR);
|
||||
}
|
||||
# Open the wizard.
|
||||
if (my $result = Slic3r::GUI::ConfigWizard->new($self, \@profiles, $fresh_start)->run) {
|
||||
eval {
|
||||
if ($result->{reset_user_profile}) {
|
||||
wxTheApp->{preset_bundle}->reset(1);
|
||||
}
|
||||
if (defined $result->{config}) {
|
||||
# Load and save the settings into print, filament and printer presets.
|
||||
wxTheApp->{preset_bundle}->load_config('My Settings', $result->{config});
|
||||
} else {
|
||||
# Wizard returned a name of a preset bundle bundled with the installation. Unpack it.
|
||||
wxTheApp->{preset_bundle}->load_configbundle($directory . '/' . $result->{preset_name} . '.ini');
|
||||
}
|
||||
};
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# This is called when closing the application, when loading a config file or when starting the config wizard
|
||||
# to notify the user whether he is aware that some preset changes will be lost.
|
||||
sub check_unsaved_changes {
|
||||
my $self = shift;
|
||||
|
||||
my @dirty = ();
|
||||
foreach my $tab (values %{$self->{options_tabs}}) {
|
||||
push @dirty, $tab->title if $tab->current_preset_is_dirty;
|
||||
}
|
||||
|
||||
if (@dirty) {
|
||||
my $titles = join ', ', @dirty;
|
||||
my $confirm = Wx::MessageDialog->new($self, L("You have unsaved changes ").($titles).L(". Discard changes and continue anyway?"),
|
||||
L('Unsaved Presets'), wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
|
||||
return $confirm->ShowModal == wxID_YES;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub select_tab {
|
||||
my ($self, $tab) = @_;
|
||||
$self->{tabpanel}->SetSelection($tab);
|
||||
|
@ -552,8 +552,9 @@ sub BUILD {
|
||||
$sizer->Add($textctrl, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
|
||||
EVT_SLIDER($self->parent, $slider, sub {
|
||||
if (! $self->disable_change_event) {
|
||||
$self->textctrl->SetLabel($self->get_value);
|
||||
if (! $self->disable_change_event) {
|
||||
# wxTextCtrl::SetLabel() does not work on Linux, use wxTextCtrl::SetValue() instead
|
||||
$self->textctrl->SetValue($self->get_value);
|
||||
$self->_on_change($self->option->opt_id);
|
||||
}
|
||||
});
|
||||
|
@ -222,7 +222,7 @@ sub mouse_event {
|
||||
];
|
||||
$self->{drag_object} = [ $obj_idx, $instance_idx ];
|
||||
} elsif ($event->RightDown) {
|
||||
$self->{on_right_click}->($pos);
|
||||
$self->{on_right_click}->($pos->x, $pos->y);
|
||||
}
|
||||
|
||||
last OBJECTS;
|
||||
@ -231,8 +231,9 @@ sub mouse_event {
|
||||
}
|
||||
$self->Refresh;
|
||||
} elsif ($event->LeftUp) {
|
||||
$self->{on_instances_moved}->()
|
||||
if $self->{drag_object};
|
||||
if ($self->{drag_object}) {
|
||||
$self->{on_instances_moved}->();
|
||||
}
|
||||
$self->{drag_start_pos} = undef;
|
||||
$self->{drag_object} = undef;
|
||||
$self->SetCursor(wxSTANDARD_CURSOR);
|
||||
|
@ -193,11 +193,11 @@ sub new {
|
||||
my $old_zoom = $self->_zoom;
|
||||
|
||||
# Calculate the zoom delta and apply it to the current zoom factor
|
||||
my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta();
|
||||
my $zoom = -$e->GetWheelRotation() / $e->GetWheelDelta();
|
||||
$zoom = max(min($zoom, 4), -4);
|
||||
$zoom /= 10;
|
||||
$self->_zoom($self->_zoom / (1-$zoom));
|
||||
$self->_zoom(1) if $self->_zoom > 1; # prevent from zooming out too much
|
||||
$self->_zoom(1.25) if $self->_zoom > 1.25; # prevent from zooming out too much
|
||||
|
||||
{
|
||||
# In order to zoom around the mouse point we need to translate
|
||||
@ -221,7 +221,6 @@ sub new {
|
||||
}
|
||||
|
||||
$self->_dirty(1);
|
||||
$self->Refresh;
|
||||
});
|
||||
EVT_MOUSE_EVENTS($self, \&mouse_event);
|
||||
|
||||
@ -249,8 +248,8 @@ sub mouse_event {
|
||||
return if !$self->GetParent->enabled;
|
||||
|
||||
my $pos = Slic3r::Pointf->new($e->GetPositionXY);
|
||||
if ($e->Entering && &Wx::wxMSW) {
|
||||
# wxMSW needs focus in order to catch mouse wheel events
|
||||
if ($e->Entering && (&Wx::wxMSW || $^O eq 'linux')) {
|
||||
# wxMSW and Linux needs focus in order to catch key events
|
||||
$self->SetFocus;
|
||||
} elsif ($e->Dragging) {
|
||||
if ($e->LeftIsDown || $e->MiddleIsDown || $e->RightIsDown) {
|
||||
@ -270,7 +269,6 @@ sub mouse_event {
|
||||
);
|
||||
|
||||
$self->_dirty(1);
|
||||
$self->Refresh;
|
||||
}
|
||||
$self->_drag_start_xy($pos);
|
||||
}
|
||||
@ -625,6 +623,27 @@ sub Resize {
|
||||
glLoadIdentity();
|
||||
|
||||
my $bb = $self->bb->clone;
|
||||
|
||||
# rescale in dependence of window aspect ratio
|
||||
my $bb_size = $bb->size;
|
||||
my $ratio_x = ($x != 0.0) ? $bb_size->x / $x : 1.0;
|
||||
my $ratio_y = ($y != 0.0) ? $bb_size->y / $y : 1.0;
|
||||
|
||||
if ($ratio_y < $ratio_x) {
|
||||
if ($ratio_y != 0.0) {
|
||||
my $new_size_y = $bb_size->y * $ratio_x / $ratio_y;
|
||||
my $half_delta_size_y = 0.5 * ($new_size_y - $bb_size->y);
|
||||
$bb->set_y_min($bb->y_min - $half_delta_size_y);
|
||||
$bb->set_y_max($bb->y_max + $half_delta_size_y);
|
||||
}
|
||||
} elsif ($ratio_x < $ratio_y) {
|
||||
if ($ratio_x != 0.0) {
|
||||
my $new_size_x = $bb_size->x * $ratio_y / $ratio_x;
|
||||
my $half_delta_size_x = 0.5 * ($new_size_x - $bb_size->x);
|
||||
$bb->set_x_min($bb->x_min - $half_delta_size_x);
|
||||
$bb->set_x_max($bb->x_max + $half_delta_size_x);
|
||||
}
|
||||
}
|
||||
|
||||
# center bounding box around origin before scaling it
|
||||
my $bb_center = $bb->center;
|
||||
@ -639,25 +658,25 @@ sub Resize {
|
||||
# translate camera
|
||||
$bb->translate(@{$self->_camera_target});
|
||||
|
||||
# keep camera_bb within total bb
|
||||
# (i.e. prevent user from panning outside the bounding box)
|
||||
{
|
||||
my @translate = (0,0);
|
||||
if ($bb->x_min < $self->bb->x_min) {
|
||||
$translate[X] += $self->bb->x_min - $bb->x_min;
|
||||
}
|
||||
if ($bb->y_min < $self->bb->y_min) {
|
||||
$translate[Y] += $self->bb->y_min - $bb->y_min;
|
||||
}
|
||||
if ($bb->x_max > $self->bb->x_max) {
|
||||
$translate[X] -= $bb->x_max - $self->bb->x_max;
|
||||
}
|
||||
if ($bb->y_max > $self->bb->y_max) {
|
||||
$translate[Y] -= $bb->y_max - $self->bb->y_max;
|
||||
}
|
||||
$self->_camera_target->translate(@translate);
|
||||
$bb->translate(@translate);
|
||||
}
|
||||
# # keep camera_bb within total bb
|
||||
# # (i.e. prevent user from panning outside the bounding box)
|
||||
# {
|
||||
# my @translate = (0,0);
|
||||
# if ($bb->x_min < $self->bb->x_min) {
|
||||
# $translate[X] += $self->bb->x_min - $bb->x_min;
|
||||
# }
|
||||
# if ($bb->y_min < $self->bb->y_min) {
|
||||
# $translate[Y] += $self->bb->y_min - $bb->y_min;
|
||||
# }
|
||||
# if ($bb->x_max > $self->bb->x_max) {
|
||||
# $translate[X] -= $bb->x_max - $self->bb->x_max;
|
||||
# }
|
||||
# if ($bb->y_max > $self->bb->y_max) {
|
||||
# $translate[Y] -= $bb->y_max - $self->bb->y_max;
|
||||
# }
|
||||
# $self->_camera_target->translate(@translate);
|
||||
# $bb->translate(@translate);
|
||||
# }
|
||||
|
||||
# save camera
|
||||
$self->_camera_bb($bb);
|
||||
|
@ -5,235 +5,22 @@ use utf8;
|
||||
|
||||
use List::Util qw();
|
||||
use Wx qw(:misc :pen :brush :sizer :font :cursor :keycode wxTAB_TRAVERSAL);
|
||||
use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR);
|
||||
use base qw(Slic3r::GUI::3DScene Class::Accessor);
|
||||
|
||||
use Wx::Locale gettext => 'L';
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(
|
||||
on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly
|
||||
on_remove_object on_increase_objects on_decrease_objects));
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $objects, $model, $print, $config) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent);
|
||||
$self->enable_picking(1);
|
||||
$self->enable_moving(1);
|
||||
$self->select_by('object');
|
||||
$self->drag_by('instance');
|
||||
|
||||
$self->{objects} = $objects;
|
||||
$self->{model} = $model;
|
||||
$self->{print} = $print;
|
||||
$self->{config} = $config;
|
||||
$self->{on_select_object} = sub {};
|
||||
$self->{on_instances_moved} = sub {};
|
||||
$self->{on_wipe_tower_moved} = sub {};
|
||||
|
||||
$self->on_select(sub {
|
||||
my ($volume_idx) = @_;
|
||||
$self->{on_select_object}->(($volume_idx == -1) ? undef : $self->volumes->[$volume_idx]->object_idx)
|
||||
if ($self->{on_select_object});
|
||||
});
|
||||
$self->on_move(sub {
|
||||
my @volume_idxs = @_;
|
||||
|
||||
my %done = (); # prevent moving instances twice
|
||||
my $object_moved;
|
||||
my $wipe_tower_moved;
|
||||
foreach my $volume_idx (@volume_idxs) {
|
||||
my $volume = $self->volumes->[$volume_idx];
|
||||
my $obj_idx = $volume->object_idx;
|
||||
my $instance_idx = $volume->instance_idx;
|
||||
next if $done{"${obj_idx}_${instance_idx}"};
|
||||
$done{"${obj_idx}_${instance_idx}"} = 1;
|
||||
if ($obj_idx < 1000) {
|
||||
# Move a regular object.
|
||||
my $model_object = $self->{model}->get_object($obj_idx);
|
||||
$model_object
|
||||
->instances->[$instance_idx]
|
||||
->offset
|
||||
->translate($volume->origin->x, $volume->origin->y); #))
|
||||
$model_object->invalidate_bounding_box;
|
||||
$object_moved = 1;
|
||||
} elsif ($obj_idx == 1000) {
|
||||
# Move a wipe tower proxy.
|
||||
$wipe_tower_moved = $volume->origin;
|
||||
}
|
||||
}
|
||||
|
||||
$self->{on_instances_moved}->()
|
||||
if $object_moved && $self->{on_instances_moved};
|
||||
$self->{on_wipe_tower_moved}->($wipe_tower_moved)
|
||||
if $wipe_tower_moved && $self->{on_wipe_tower_moved};
|
||||
});
|
||||
|
||||
EVT_KEY_DOWN($self, sub {
|
||||
my ($s, $event) = @_;
|
||||
if ($event->HasModifiers) {
|
||||
$event->Skip;
|
||||
} else {
|
||||
my $key = $event->GetKeyCode;
|
||||
if ($key == WXK_DELETE) {
|
||||
$self->on_remove_object->() if $self->on_remove_object;
|
||||
} else {
|
||||
$event->Skip;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
EVT_CHAR($self, sub {
|
||||
my ($s, $event) = @_;
|
||||
if ($event->HasModifiers) {
|
||||
$event->Skip;
|
||||
} else {
|
||||
my $key = $event->GetKeyCode;
|
||||
if ($key == ord('a')) {
|
||||
$self->on_arrange->() if $self->on_arrange;
|
||||
} elsif ($key == ord('l')) {
|
||||
$self->on_rotate_object_left->() if $self->on_rotate_object_left;
|
||||
} elsif ($key == ord('r')) {
|
||||
$self->on_rotate_object_right->() if $self->on_rotate_object_right;
|
||||
} elsif ($key == ord('s')) {
|
||||
$self->on_scale_object_uniformly->() if $self->on_scale_object_uniformly;
|
||||
} elsif ($key == ord('+')) {
|
||||
$self->on_increase_objects->() if $self->on_increase_objects;
|
||||
} elsif ($key == ord('-')) {
|
||||
$self->on_decrease_objects->() if $self->on_decrease_objects;
|
||||
} else {
|
||||
$event->Skip;
|
||||
}
|
||||
}
|
||||
});
|
||||
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;
|
||||
}
|
||||
|
||||
sub set_on_select_object {
|
||||
my ($self, $cb) = @_;
|
||||
$self->{on_select_object} = $cb;
|
||||
}
|
||||
|
||||
sub set_on_double_click {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_double_click($cb);
|
||||
}
|
||||
|
||||
sub set_on_right_click {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_right_click($cb);
|
||||
}
|
||||
|
||||
sub set_on_arrange {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_arrange($cb);
|
||||
}
|
||||
|
||||
sub set_on_rotate_object_left {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_rotate_object_left($cb);
|
||||
}
|
||||
|
||||
sub set_on_rotate_object_right {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_rotate_object_right($cb);
|
||||
}
|
||||
|
||||
sub set_on_scale_object_uniformly {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_scale_object_uniformly($cb);
|
||||
}
|
||||
|
||||
sub set_on_increase_objects {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_increase_objects($cb);
|
||||
}
|
||||
|
||||
sub set_on_decrease_objects {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_decrease_objects($cb);
|
||||
}
|
||||
|
||||
sub set_on_remove_object {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_remove_object($cb);
|
||||
}
|
||||
|
||||
sub set_on_instances_moved {
|
||||
my ($self, $cb) = @_;
|
||||
$self->{on_instances_moved} = $cb;
|
||||
}
|
||||
|
||||
sub set_on_wipe_tower_moved {
|
||||
my ($self, $cb) = @_;
|
||||
$self->{on_wipe_tower_moved} = $cb;
|
||||
}
|
||||
|
||||
sub set_on_model_update {
|
||||
my ($self, $cb) = @_;
|
||||
$self->on_model_update($cb);
|
||||
}
|
||||
|
||||
sub reload_scene {
|
||||
my ($self, $force) = @_;
|
||||
|
||||
$self->reset_objects;
|
||||
$self->update_bed_size;
|
||||
|
||||
if (! $self->IsShown && ! $force) {
|
||||
$self->{reload_delayed} = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
$self->{reload_delayed} = 0;
|
||||
|
||||
foreach my $obj_idx (0..$#{$self->{model}->objects}) {
|
||||
my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx);
|
||||
if ($self->{objects}[$obj_idx]->selected) {
|
||||
$self->select_volume($_) for @volume_idxs;
|
||||
}
|
||||
}
|
||||
if (defined $self->{config}->nozzle_diameter) {
|
||||
# Should the wipe tower be visualized?
|
||||
my $extruders_count = scalar @{ $self->{config}->nozzle_diameter };
|
||||
# Height of a print.
|
||||
my $height = $self->{model}->bounding_box->z_max;
|
||||
# Show at least a slab.
|
||||
$height = 10 if $height < 10;
|
||||
if ($extruders_count > 1 && $self->{config}->single_extruder_multi_material && $self->{config}->wipe_tower &&
|
||||
! $self->{config}->complete_objects) {
|
||||
$self->volumes->load_wipe_tower_preview(1000,
|
||||
$self->{config}->wipe_tower_x, $self->{config}->wipe_tower_y,
|
||||
$self->{config}->wipe_tower_width, $self->{config}->wipe_tower_per_color_wipe * ($extruders_count - 1),
|
||||
$self->{model}->bounding_box->z_max, $self->UseVBOs);
|
||||
}
|
||||
}
|
||||
|
||||
# checks for geometry outside the print volume to render it accordingly
|
||||
if (scalar @{$self->volumes} > 0)
|
||||
{
|
||||
if (!$self->{model}->fits_print_volume($self->{config})) {
|
||||
$self->set_warning_enabled(1);
|
||||
Slic3r::GUI::_3DScene::generate_warning_texture(L("Detected object outside print volume"));
|
||||
} else {
|
||||
$self->set_warning_enabled(0);
|
||||
$self->volumes->update_outside_state($self->{config}, 1);
|
||||
Slic3r::GUI::_3DScene::reset_warning_texture();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub update_bed_size {
|
||||
my ($self) = @_;
|
||||
$self->set_bed_shape($self->{config}->bed_shape);
|
||||
}
|
||||
|
||||
# Called by the Platter wxNotebook when this page is activated.
|
||||
sub OnActivate {
|
||||
my ($self) = @_;
|
||||
$self->reload_scene(1) if ($self->{reload_delayed});
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -10,7 +10,7 @@ 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 auto_zoom));
|
||||
__PACKAGE__->mk_accessors(qw(print gcode_preview_data enabled _loaded canvas slider_low slider_high single_layer));
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
@ -21,11 +21,11 @@ sub new {
|
||||
$self->{number_extruders} = 1;
|
||||
# Show by feature type by default.
|
||||
$self->{preferred_color_mode} = 'feature';
|
||||
$self->auto_zoom(1);
|
||||
|
||||
# init GUI elements
|
||||
my $canvas = Slic3r::GUI::3DScene->new($self);
|
||||
$canvas->use_plain_shader(1);
|
||||
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,
|
||||
@ -59,6 +59,13 @@ sub new {
|
||||
[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"));
|
||||
|
||||
@ -69,9 +76,13 @@ sub new {
|
||||
$choice_view_type->Append(L("Height"));
|
||||
$choice_view_type->Append(L("Width"));
|
||||
$choice_view_type->Append(L("Speed"));
|
||||
$choice_view_type->Append(L("Volumetric flow rate"));
|
||||
$choice_view_type->Append(L("Tool"));
|
||||
$choice_view_type->SetSelection(0);
|
||||
|
||||
# the following value needs to be changed if new items are added into $choice_view_type before "Tool"
|
||||
$self->{tool_idx} = 5;
|
||||
|
||||
my $label_show_features = $self->{label_show_features} = Wx::StaticText->new($self, -1, L("Show"));
|
||||
|
||||
my $combochecklist_features = $self->{combochecklist_features} = Wx::ComboCtrl->new();
|
||||
@ -100,11 +111,13 @@ sub new {
|
||||
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
my $vsizer_outer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$vsizer->Add($slider_low, 3, 0, 0);
|
||||
$vsizer->Add($z_label_low, 0, 0, 0);
|
||||
$vsizer->Add($slider_low, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
$vsizer->Add($z_label_low_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
$vsizer->Add($z_label_low, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
$hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
||||
$vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$vsizer->Add($slider_high, 3, 0, 0);
|
||||
$vsizer->Add($slider_high, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
$vsizer->Add($z_label_high_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
$vsizer->Add($z_label_high, 0, 0, 0);
|
||||
$hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
||||
$vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
@ -204,43 +217,31 @@ sub new {
|
||||
});
|
||||
EVT_CHOICE($self, $choice_view_type, sub {
|
||||
my $selection = $choice_view_type->GetCurrentSelection();
|
||||
$self->{preferred_color_mode} = ($selection == 4) ? 'tool' : 'feature';
|
||||
$self->{preferred_color_mode} = ($selection == $self->{tool_idx}) ? 'tool' : 'feature';
|
||||
$self->gcode_preview_data->set_type($selection);
|
||||
$self->auto_zoom(0);
|
||||
$self->reload_print;
|
||||
$self->auto_zoom(1);
|
||||
});
|
||||
EVT_CHECKLISTBOX($self, $combochecklist_features, sub {
|
||||
my $flags = Slic3r::GUI::combochecklist_get_flags($combochecklist_features);
|
||||
|
||||
$self->gcode_preview_data->set_extrusion_flags($flags);
|
||||
$self->auto_zoom(0);
|
||||
$self->refresh_print;
|
||||
$self->auto_zoom(1);
|
||||
});
|
||||
EVT_CHECKBOX($self, $checkbox_travel, sub {
|
||||
$self->gcode_preview_data->set_travel_visible($checkbox_travel->IsChecked());
|
||||
$self->auto_zoom(0);
|
||||
$self->refresh_print;
|
||||
$self->auto_zoom(1);
|
||||
});
|
||||
EVT_CHECKBOX($self, $checkbox_retractions, sub {
|
||||
$self->gcode_preview_data->set_retractions_visible($checkbox_retractions->IsChecked());
|
||||
$self->auto_zoom(0);
|
||||
$self->refresh_print;
|
||||
$self->auto_zoom(1);
|
||||
});
|
||||
EVT_CHECKBOX($self, $checkbox_unretractions, sub {
|
||||
$self->gcode_preview_data->set_unretractions_visible($checkbox_unretractions->IsChecked());
|
||||
$self->auto_zoom(0);
|
||||
$self->refresh_print;
|
||||
$self->auto_zoom(1);
|
||||
});
|
||||
EVT_CHECKBOX($self, $checkbox_shells, sub {
|
||||
$self->gcode_preview_data->set_shells_visible($checkbox_shells->IsChecked());
|
||||
$self->auto_zoom(0);
|
||||
$self->refresh_print;
|
||||
$self->auto_zoom(1);
|
||||
});
|
||||
|
||||
$self->SetSizer($main_sizer);
|
||||
@ -277,8 +278,8 @@ sub new {
|
||||
|
||||
sub reload_print {
|
||||
my ($self, $force) = @_;
|
||||
|
||||
$self->canvas->reset_objects;
|
||||
|
||||
Slic3r::GUI::_3DScene::reset_volumes($self->canvas);
|
||||
$self->_loaded(0);
|
||||
|
||||
if (! $self->IsShown && ! $force) {
|
||||
@ -301,6 +302,12 @@ sub refresh_print {
|
||||
$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) = @_;
|
||||
|
||||
@ -322,25 +329,17 @@ sub load_print {
|
||||
}
|
||||
|
||||
if ($n_layers == 0) {
|
||||
$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->canvas->reset_legend_texture();
|
||||
$self->reset_sliders;
|
||||
Slic3r::GUI::_3DScene::reset_legend_texture();
|
||||
$self->canvas->Refresh; # clears canvas
|
||||
return;
|
||||
}
|
||||
|
||||
# used to set the sliders to the extremes of the current zs range
|
||||
$self->{force_sliders_full_range} = 0;
|
||||
|
||||
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) ? 4 : 0;
|
||||
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".
|
||||
@ -349,7 +348,7 @@ sub load_print {
|
||||
|
||||
# Collect colors per extruder.
|
||||
my @colors = ();
|
||||
if (! $self->gcode_preview_data->empty() || $self->gcode_preview_data->type == 4) {
|
||||
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) {
|
||||
@ -361,36 +360,47 @@ sub load_print {
|
||||
}
|
||||
|
||||
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
|
||||
$self->canvas->load_print_toolpaths($self->print, \@colors);
|
||||
$self->canvas->load_wipe_tower_toolpaths($self->print, \@colors);
|
||||
foreach my $object (@{$self->print->objects}) {
|
||||
$self->canvas->load_print_object_toolpaths($object, \@colors);
|
||||
# Show the objects in very transparent color.
|
||||
#my @volume_ids = $self->canvas->load_object($object->model_object);
|
||||
#$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids;
|
||||
}
|
||||
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} = (scalar(@{$self->canvas->volumes}) == 0) && $self->auto_zoom;
|
||||
$self->canvas->load_gcode_preview($self->print, $self->gcode_preview_data, \@colors);
|
||||
$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} = $self->canvas->get_current_print_zs();
|
||||
$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 ($self->auto_zoom) {
|
||||
$self->canvas->zoom_to_volumes;
|
||||
}
|
||||
$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("");
|
||||
}
|
||||
|
||||
sub update_sliders
|
||||
{
|
||||
my ($self, $n_layers) = @_;
|
||||
@ -405,18 +415,32 @@ sub update_sliders
|
||||
$z_idx_low = 0;
|
||||
$z_idx_high = $n_layers - 1;
|
||||
} elsif ($z_idx_high < $n_layers && ($self->single_layer || $z_idx_high != 0)) {
|
||||
# use $z_idx
|
||||
} else {
|
||||
# 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;
|
||||
}
|
||||
if ($self->single_layer) {
|
||||
$z_idx_low = $z_idx_high;
|
||||
} elsif ($z_idx_low > $z_idx_high) {
|
||||
} else {
|
||||
$z_idx_low = 0;
|
||||
$z_idx_high = $n_layers - 1;
|
||||
}
|
||||
|
||||
$self->slider_low->SetValue($z_idx_low);
|
||||
@ -432,9 +456,26 @@ 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);
|
||||
$self->canvas->set_toolpaths_range($z_low - 1e-6, $z_high + 1e-6);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -464,21 +505,16 @@ sub set_z_idx_high
|
||||
}
|
||||
}
|
||||
|
||||
sub set_bed_shape {
|
||||
my ($self, $bed_shape) = @_;
|
||||
$self->canvas->set_bed_shape($bed_shape);
|
||||
}
|
||||
|
||||
sub set_number_extruders {
|
||||
my ($self, $number_extruders) = @_;
|
||||
if ($self->{number_extruders} != $number_extruders) {
|
||||
$self->{number_extruders} = $number_extruders;
|
||||
my $type = ($number_extruders > 1) ?
|
||||
4 # color by a tool number
|
||||
$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 == 4) ? 'tool_or_feature' : 'feature';
|
||||
$self->{preferred_color_mode} = ($type == $self->{tool_idx}) ? 'tool_or_feature' : 'feature';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ use utf8;
|
||||
use Slic3r::Geometry qw(PI X);
|
||||
use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_BUTTON);
|
||||
use List::Util qw(max);
|
||||
use base 'Wx::Dialog';
|
||||
|
||||
sub new {
|
||||
@ -60,7 +61,7 @@ sub new {
|
||||
label => 'Z',
|
||||
default => $self->{cut_options}{z},
|
||||
min => 0,
|
||||
max => $self->{model_object}->bounding_box->size->z * $self->{model_object}->instances->[0]->scaling_factor,
|
||||
max => $self->{model_object}->bounding_box->size->z,
|
||||
full_width => 1,
|
||||
));
|
||||
{
|
||||
@ -112,11 +113,13 @@ sub new {
|
||||
my $canvas;
|
||||
if ($Slic3r::GUI::have_OpenGL) {
|
||||
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
|
||||
$canvas->load_object($self->{model_object}, undef, undef, [0]);
|
||||
$canvas->set_auto_bed_shape;
|
||||
Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]);
|
||||
Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas);
|
||||
Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size }));
|
||||
$canvas->SetSize([500,500]);
|
||||
$canvas->SetMinSize($canvas->GetSize);
|
||||
$canvas->zoom_to_volumes;
|
||||
Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->{config});
|
||||
Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1);
|
||||
}
|
||||
|
||||
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
@ -134,7 +137,7 @@ sub new {
|
||||
# Adjust position / orientation of the split object halves.
|
||||
if ($self->{new_model_objects}{lower}) {
|
||||
if ($self->{cut_options}{rotate_lower}) {
|
||||
$self->{new_model_objects}{lower}->rotate(PI, X);
|
||||
$self->{new_model_objects}{lower}->rotate(PI, Slic3r::Pointf3->new(1,0,0));
|
||||
$self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0
|
||||
}
|
||||
}
|
||||
@ -145,6 +148,7 @@ sub new {
|
||||
# Note that the window was already closed, so a pending update will not be executed.
|
||||
$self->{already_closed} = 1;
|
||||
$self->EndModal(wxID_OK);
|
||||
$self->{canvas}->Destroy;
|
||||
$self->Destroy();
|
||||
});
|
||||
|
||||
@ -152,6 +156,7 @@ sub new {
|
||||
# Note that the window was already closed, so a pending update will not be executed.
|
||||
$self->{already_closed} = 1;
|
||||
$self->EndModal(wxID_CANCEL);
|
||||
$self->{canvas}->Destroy;
|
||||
$self->Destroy();
|
||||
});
|
||||
|
||||
@ -227,12 +232,14 @@ sub _update {
|
||||
push @objects, $self->{model_object};
|
||||
}
|
||||
|
||||
my $z_cut = $z + $self->{model_object}->bounding_box->z_min;
|
||||
|
||||
# get section contour
|
||||
my @expolygons = ();
|
||||
foreach my $volume (@{$self->{model_object}->volumes}) {
|
||||
next if !$volume->mesh;
|
||||
next if $volume->modifier;
|
||||
my $expp = $volume->mesh->slice([ $z + $volume->mesh->bounding_box->z_min ])->[0];
|
||||
my $expp = $volume->mesh->slice([ $z_cut ])->[0];
|
||||
push @expolygons, @$expp;
|
||||
}
|
||||
foreach my $expolygon (@expolygons) {
|
||||
@ -240,14 +247,12 @@ sub _update {
|
||||
for @$expolygon;
|
||||
$expolygon->translate(map Slic3r::Geometry::scale($_), @{ $self->{model_object}->instances->[0]->offset });
|
||||
}
|
||||
|
||||
$self->{canvas}->reset_objects;
|
||||
$self->{canvas}->load_object($_, undef, undef, [0]) for @objects;
|
||||
$self->{canvas}->SetCuttingPlane(
|
||||
$self->{cut_options}{z},
|
||||
[@expolygons],
|
||||
);
|
||||
$self->{canvas}->Render;
|
||||
|
||||
Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects;
|
||||
Slic3r::GUI::_3DScene::set_cutting_plane($self->{canvas}, $self->{cut_options}{z}, [@expolygons]);
|
||||
Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::render($self->{canvas});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,8 @@ use utf8;
|
||||
use File::Basename qw(basename);
|
||||
use Wx qw(:misc :sizer :treectrl :button :keycode wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL wxMOD_CONTROL
|
||||
wxTheApp);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN EVT_KEY_DOWN);
|
||||
use List::Util qw(max);
|
||||
use base 'Wx::Panel';
|
||||
|
||||
use constant ICON_OBJECT => 0;
|
||||
@ -88,7 +89,7 @@ sub new {
|
||||
$self->{btn_move_down}->SetFont($Slic3r::GUI::small_font);
|
||||
|
||||
# part settings panel
|
||||
$self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; });
|
||||
$self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; $self->_update_canvas; });
|
||||
my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL);
|
||||
$settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0);
|
||||
|
||||
@ -150,19 +151,19 @@ sub new {
|
||||
my $canvas;
|
||||
if ($Slic3r::GUI::have_OpenGL) {
|
||||
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
|
||||
$canvas->enable_picking(1);
|
||||
$canvas->select_by('volume');
|
||||
|
||||
$canvas->on_select(sub {
|
||||
Slic3r::GUI::_3DScene::enable_picking($canvas, 1);
|
||||
Slic3r::GUI::_3DScene::set_select_by($canvas, 'volume');
|
||||
Slic3r::GUI::_3DScene::register_on_select_object_callback($canvas, sub {
|
||||
my ($volume_idx) = @_;
|
||||
# convert scene volume to model object volume
|
||||
$self->reload_tree(($volume_idx == -1) ? undef : $canvas->volumes->[$volume_idx]->volume_idx);
|
||||
$self->reload_tree($volume_idx);
|
||||
});
|
||||
|
||||
$canvas->load_object($self->{model_object}, undef, undef, [0]);
|
||||
$canvas->set_auto_bed_shape;
|
||||
Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]);
|
||||
Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas);
|
||||
Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size }));
|
||||
$canvas->SetSize([500,700]);
|
||||
$canvas->zoom_to_volumes;
|
||||
Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->GetParent->GetParent->{config});
|
||||
Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($canvas);
|
||||
Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1);
|
||||
}
|
||||
|
||||
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
@ -190,6 +191,14 @@ sub new {
|
||||
EVT_BUTTON($self, $self->{btn_split}, \&on_btn_split);
|
||||
EVT_BUTTON($self, $self->{btn_move_up}, \&on_btn_move_up);
|
||||
EVT_BUTTON($self, $self->{btn_move_down}, \&on_btn_move_down);
|
||||
EVT_KEY_DOWN($canvas, sub {
|
||||
my ($canvas, $event) = @_;
|
||||
if ($event->GetKeyCode == WXK_DELETE) {
|
||||
$canvas->GetParent->on_btn_delete;
|
||||
} else {
|
||||
$event->Skip;
|
||||
}
|
||||
});
|
||||
|
||||
$self->reload_tree;
|
||||
|
||||
@ -254,7 +263,7 @@ sub selection_changed {
|
||||
|
||||
# deselect all meshes
|
||||
if ($self->{canvas}) {
|
||||
$_->set_selected(0) for @{$self->{canvas}->volumes};
|
||||
Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas});
|
||||
}
|
||||
|
||||
# disable things as if nothing is selected
|
||||
@ -282,7 +291,7 @@ sub selection_changed {
|
||||
if ($itemData->{type} eq 'volume') {
|
||||
# select volume in 3D preview
|
||||
if ($self->{canvas}) {
|
||||
$self->{canvas}->volumes->[ $itemData->{volume_id} ]->set_selected(1);
|
||||
Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id});
|
||||
}
|
||||
$self->{btn_delete}->Enable;
|
||||
$self->{btn_split}->Enable;
|
||||
@ -313,7 +322,13 @@ sub selection_changed {
|
||||
}
|
||||
# get default values
|
||||
my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
|
||||
|
||||
|
||||
# decide which settings will be shown by default
|
||||
if ($itemData->{type} eq 'object') {
|
||||
$config->set_ifndef('wipe_into_objects', 0);
|
||||
$config->set_ifndef('wipe_into_infill', 0);
|
||||
}
|
||||
|
||||
# append default extruder
|
||||
push @opt_keys, 'extruder';
|
||||
$default_config->set('extruder', 0);
|
||||
@ -321,11 +336,18 @@ sub selection_changed {
|
||||
$self->{settings_panel}->set_default_config($default_config);
|
||||
$self->{settings_panel}->set_config($config);
|
||||
$self->{settings_panel}->set_opt_keys(\@opt_keys);
|
||||
$self->{settings_panel}->set_fixed_options([qw(extruder)]);
|
||||
|
||||
# disable minus icon to remove the settings
|
||||
if ($itemData->{type} eq 'object') {
|
||||
$self->{settings_panel}->set_fixed_options([qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)]);
|
||||
} else {
|
||||
$self->{settings_panel}->set_fixed_options([qw(extruder)]);
|
||||
}
|
||||
|
||||
$self->{settings_panel}->enable;
|
||||
}
|
||||
|
||||
$self->{canvas}->Render if $self->{canvas};
|
||||
Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas};
|
||||
}
|
||||
|
||||
sub on_btn_load {
|
||||
@ -355,7 +377,8 @@ sub on_btn_load {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$self->{model_object}->center_around_origin if $self->{parts_changed};
|
||||
$self->_parts_changed;
|
||||
}
|
||||
|
||||
@ -400,14 +423,17 @@ sub on_tree_key_down {
|
||||
my ($self, $event) = @_;
|
||||
my $keycode = $event->GetKeyCode;
|
||||
# Wx >= 0.9911
|
||||
if (defined(&Wx::TreeEvent::GetKeyEvent) &&
|
||||
($event->GetKeyEvent->GetModifiers & wxMOD_CONTROL)) {
|
||||
if ($keycode == WXK_UP) {
|
||||
$event->Skip;
|
||||
$self->on_btn_move_up;
|
||||
} elsif ($keycode == WXK_DOWN) {
|
||||
$event->Skip;
|
||||
$self->on_btn_move_down;
|
||||
if (defined(&Wx::TreeEvent::GetKeyEvent)) {
|
||||
if ($event->GetKeyEvent->GetModifiers & wxMOD_CONTROL) {
|
||||
if ($keycode == WXK_UP) {
|
||||
$event->Skip;
|
||||
$self->on_btn_move_up;
|
||||
} elsif ($keycode == WXK_DOWN) {
|
||||
$event->Skip;
|
||||
$self->on_btn_move_down;
|
||||
}
|
||||
} elsif ($keycode == WXK_DELETE) {
|
||||
$self->on_btn_delete;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -418,7 +444,7 @@ sub on_btn_move_up {
|
||||
if ($itemData && $itemData->{type} eq 'volume') {
|
||||
my $volume_id = $itemData->{volume_id};
|
||||
if ($self->{model_object}->move_volume_up($volume_id)) {
|
||||
$self->{canvas}->volumes->move_volume_up($volume_id);
|
||||
Slic3r::GUI::_3DScene::move_volume_up($self->{canvas}, $volume_id);
|
||||
$self->{parts_changed} = 1;
|
||||
$self->reload_tree($volume_id - 1);
|
||||
}
|
||||
@ -431,7 +457,7 @@ sub on_btn_move_down {
|
||||
if ($itemData && $itemData->{type} eq 'volume') {
|
||||
my $volume_id = $itemData->{volume_id};
|
||||
if ($self->{model_object}->move_volume_down($volume_id)) {
|
||||
$self->{canvas}->volumes->move_volume_down($volume_id);
|
||||
Slic3r::GUI::_3DScene::move_volume_down($self->{canvas}, $volume_id);
|
||||
$self->{parts_changed} = 1;
|
||||
$self->reload_tree($volume_id + 1);
|
||||
}
|
||||
@ -454,7 +480,8 @@ sub on_btn_delete {
|
||||
$self->{model_object}->delete_volume($itemData->{volume_id});
|
||||
$self->{parts_changed} = 1;
|
||||
}
|
||||
|
||||
|
||||
$self->{model_object}->center_around_origin if $self->{parts_changed};
|
||||
$self->_parts_changed;
|
||||
}
|
||||
|
||||
@ -464,7 +491,8 @@ sub on_btn_split {
|
||||
my $itemData = $self->get_selection;
|
||||
if ($itemData && $itemData->{type} eq 'volume') {
|
||||
my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}];
|
||||
$self->{parts_changed} = 1 if $volume->split > 1;
|
||||
my $nozzle_dmrs = $self->GetParent->GetParent->GetParent->{config}->get('nozzle_diameter');
|
||||
$self->{parts_changed} = 1 if $volume->split(scalar(@$nozzle_dmrs)) > 1;
|
||||
}
|
||||
|
||||
$self->_parts_changed;
|
||||
@ -475,10 +503,11 @@ sub _parts_changed {
|
||||
|
||||
$self->reload_tree;
|
||||
if ($self->{canvas}) {
|
||||
$self->{canvas}->reset_objects;
|
||||
$self->{canvas}->load_object($self->{model_object});
|
||||
$self->{canvas}->zoom_to_volumes;
|
||||
$self->{canvas}->Render;
|
||||
Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]);
|
||||
Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::render($self->{canvas});
|
||||
}
|
||||
}
|
||||
|
||||
@ -498,6 +527,11 @@ sub CanClose {
|
||||
return ! Slic3r::GUI::catch_error($self);
|
||||
}
|
||||
|
||||
sub Destroy {
|
||||
my ($self) = @_;
|
||||
$self->{canvas}->Destroy if ($self->{canvas});
|
||||
}
|
||||
|
||||
sub PartsChanged {
|
||||
my ($self) = @_;
|
||||
return $self->{parts_changed};
|
||||
@ -508,6 +542,25 @@ sub PartSettingsChanged {
|
||||
return $self->{part_settings_changed};
|
||||
}
|
||||
|
||||
sub _update_canvas {
|
||||
my ($self) = @_;
|
||||
|
||||
if ($self->{canvas}) {
|
||||
Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]);
|
||||
|
||||
# restore selection, if any
|
||||
if (my $itemData = $self->get_selection) {
|
||||
if ($itemData->{type} eq 'volume') {
|
||||
Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id});
|
||||
}
|
||||
}
|
||||
|
||||
Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::render($self->{canvas});
|
||||
}
|
||||
}
|
||||
|
||||
sub _update {
|
||||
my ($self) = @_;
|
||||
my ($m_x, $m_y, $m_z) = ($self->{move_options}{x}, $self->{move_options}{y}, $self->{move_options}{z});
|
||||
@ -526,9 +579,10 @@ sub _update {
|
||||
$self->{parts_changed} = 1;
|
||||
my @objects = ();
|
||||
push @objects, $self->{model_object};
|
||||
$self->{canvas}->reset_objects;
|
||||
$self->{canvas}->load_object($_, undef, [0]) for @objects;
|
||||
$self->{canvas}->Render;
|
||||
Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects;
|
||||
Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::render($self->{canvas});
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -36,6 +36,7 @@ sub new {
|
||||
wxTheApp->save_window_pos($self, "object_settings");
|
||||
|
||||
$self->EndModal(wxID_OK);
|
||||
$self->{parts}->Destroy;
|
||||
$self->Destroy;
|
||||
});
|
||||
|
||||
@ -46,6 +47,8 @@ sub new {
|
||||
$self->SetSizer($sizer);
|
||||
$self->SetMinSize($self->GetSize);
|
||||
|
||||
$self->Layout;
|
||||
|
||||
wxTheApp->restore_window_pos($self, "object_settings");
|
||||
|
||||
return $self;
|
||||
|
@ -136,7 +136,7 @@ sub update_optgroup {
|
||||
full_labels => 1,
|
||||
label_font => $Slic3r::GUI::small_font,
|
||||
sidetext_font => $Slic3r::GUI::small_font,
|
||||
label_width => 120,
|
||||
label_width => 150,
|
||||
on_change => sub { $self->{on_change}->() if $self->{on_change} },
|
||||
extra_column => sub {
|
||||
my ($line) = @_;
|
||||
|
@ -1,144 +1,18 @@
|
||||
# Status bar at the bottom of the main screen.
|
||||
# Now it just implements cancel cb on perl side, every other functionality is
|
||||
# in C++
|
||||
|
||||
package Slic3r::GUI::ProgressStatusBar;
|
||||
package Slic3r::GUI::ProgressStatusBar;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Wx qw(:gauge :misc);
|
||||
use base 'Wx::StatusBar';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
|
||||
$self->{busy} = 0;
|
||||
$self->{timer} = Wx::Timer->new($self);
|
||||
$self->{prog} = Wx::Gauge->new($self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize);
|
||||
$self->{prog}->Hide;
|
||||
$self->{cancelbutton} = Wx::Button->new($self, -1, "Cancel", wxDefaultPosition, wxDefaultSize);
|
||||
$self->{cancelbutton}->Hide;
|
||||
|
||||
$self->SetFieldsCount(3);
|
||||
$self->SetStatusWidths(-1, 150, 155);
|
||||
|
||||
Wx::Event::EVT_TIMER($self, \&OnTimer, $self->{timer});
|
||||
Wx::Event::EVT_SIZE($self, \&OnSize);
|
||||
Wx::Event::EVT_BUTTON($self, $self->{cancelbutton}, sub {
|
||||
$self->{cancel_cb}->();
|
||||
$self->{cancelbutton}->Hide;
|
||||
});
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
my $self = shift;
|
||||
$self->{timer}->Stop if $self->{timer} && $self->{timer}->IsRunning;
|
||||
}
|
||||
|
||||
sub OnSize {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
my %fields = (
|
||||
# 0 is reserved for status text
|
||||
1 => $self->{cancelbutton},
|
||||
2 => $self->{prog},
|
||||
);
|
||||
|
||||
foreach (keys %fields) {
|
||||
my $rect = $self->GetFieldRect($_);
|
||||
my $offset = &Wx::wxGTK ? 1 : 0; # add a cosmetic 1 pixel offset on wxGTK
|
||||
my $pos = [$rect->GetX + $offset, $rect->GetY + $offset];
|
||||
$fields{$_}->Move($pos);
|
||||
$fields{$_}->SetSize($rect->GetWidth - $offset, $rect->GetHeight);
|
||||
}
|
||||
|
||||
$event->Skip;
|
||||
}
|
||||
|
||||
sub OnTimer {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
if ($self->{prog}->IsShown) {
|
||||
$self->{timer}->Stop;
|
||||
}
|
||||
$self->{prog}->Pulse if $self->{_busy};
|
||||
}
|
||||
our $cancel_cb;
|
||||
|
||||
sub SetCancelCallback {
|
||||
my $self = shift;
|
||||
my ($cb) = @_;
|
||||
$self->{cancel_cb} = $cb;
|
||||
$cb ? $self->{cancelbutton}->Show : $self->{cancelbutton}->Hide;
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my $self = shift;
|
||||
my $rate = shift || 100;
|
||||
if (!$self->{timer}->IsRunning) {
|
||||
$self->{timer}->Start($rate);
|
||||
}
|
||||
}
|
||||
|
||||
sub GetProgress {
|
||||
my $self = shift;
|
||||
return $self->{prog}->GetValue;
|
||||
}
|
||||
|
||||
sub SetProgress {
|
||||
my $self = shift;
|
||||
my ($val) = @_;
|
||||
if (!$self->{prog}->IsShown) {
|
||||
$self->ShowProgress(1);
|
||||
}
|
||||
if ($val == $self->{prog}->GetRange) {
|
||||
$self->{prog}->SetValue(0);
|
||||
$self->ShowProgress(0);
|
||||
} else {
|
||||
$self->{prog}->SetValue($val);
|
||||
}
|
||||
}
|
||||
|
||||
sub SetRange {
|
||||
my $self = shift;
|
||||
my ($val) = @_;
|
||||
|
||||
if ($val != $self->{prog}->GetRange) {
|
||||
$self->{prog}->SetRange($val);
|
||||
}
|
||||
}
|
||||
|
||||
sub ShowProgress {
|
||||
my $self = shift;
|
||||
my ($show) = @_;
|
||||
|
||||
$self->{prog}->Show($show);
|
||||
$self->{prog}->Pulse;
|
||||
}
|
||||
|
||||
sub StartBusy {
|
||||
my $self = shift;
|
||||
my $rate = shift || 100;
|
||||
|
||||
$self->{_busy} = 1;
|
||||
$self->ShowProgress(1);
|
||||
if (!$self->{timer}->IsRunning) {
|
||||
$self->{timer}->Start($rate);
|
||||
}
|
||||
}
|
||||
|
||||
sub StopBusy {
|
||||
my $self = shift;
|
||||
|
||||
$self->{timer}->Stop;
|
||||
$self->ShowProgress(0);
|
||||
$self->{prog}->SetValue(0);
|
||||
$self->{_busy} = 0;
|
||||
}
|
||||
|
||||
sub IsBusy {
|
||||
my $self = shift;
|
||||
return $self->{_busy};
|
||||
$cancel_cb = $cb;
|
||||
$cb ? $self->ShowCancelButton : $self->HideCancelButton;
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -35,7 +35,12 @@ sub run_post_process_scripts {
|
||||
die "The configured post-processing script is not executable: check permissions. ($script)\n";
|
||||
}
|
||||
if ($^O eq 'MSWin32' && $script =~ /\.[pP][lL]/) {
|
||||
system($^X, $script, $output_file);
|
||||
# The current process (^X) may be slic3r.exe or slic3r-console.exe.
|
||||
# Replace it with the current perl interpreter.
|
||||
my($filename, $directories, $suffix) = fileparse($^X);
|
||||
$filename =~ s/^slic3r.*$/perl5\.24\.0\.exe/;
|
||||
my $interpreter = $directories . $filename;
|
||||
system($interpreter, $script, $output_file);
|
||||
} else {
|
||||
system($script, $output_file);
|
||||
}
|
||||
@ -43,13 +48,37 @@ sub run_post_process_scripts {
|
||||
}
|
||||
}
|
||||
|
||||
sub export_png {
|
||||
my $self = shift;
|
||||
my %params = @_;
|
||||
|
||||
my @sobjects = @{$self->objects};
|
||||
my $objnum = scalar @sobjects;
|
||||
for(my $oi = 0; $oi < $objnum; $oi++)
|
||||
{
|
||||
$sobjects[$oi]->slice;
|
||||
$self->status_cb->(($oi + 1)*100/$objnum - 1, "Slicing...");
|
||||
}
|
||||
|
||||
my $fh = $params{output_file};
|
||||
$self->status_cb->(90, "Exporting zipped archive...");
|
||||
$self->print_to_png($fh);
|
||||
$self->status_cb->(100, "Done.");
|
||||
}
|
||||
|
||||
# Export SVG slices for the offline SLA printing.
|
||||
# The export_svg is expected to be executed inside an eval block.
|
||||
sub export_svg {
|
||||
my $self = shift;
|
||||
my %params = @_;
|
||||
|
||||
$_->slice for @{$self->objects};
|
||||
my @sobjects = @{$self->objects};
|
||||
my $objnum = scalar @sobjects;
|
||||
for(my $oi = 0; $oi < $objnum; $oi++)
|
||||
{
|
||||
$sobjects[$oi]->slice;
|
||||
$self->status_cb->(($oi + 1)*100/$objnum - 1, "Slicing...");
|
||||
}
|
||||
|
||||
my $fh = $params{output_fh};
|
||||
if (!$fh) {
|
||||
|
@ -97,4 +97,14 @@ sub export_svg {
|
||||
$self->_print->export_svg(output_file => $self->output_file);
|
||||
}
|
||||
|
||||
sub export_png {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_before_export;
|
||||
|
||||
$self->_print->export_png(output_file => $self->output_file);
|
||||
|
||||
$self->_after_export;
|
||||
}
|
||||
|
||||
1;
|
||||
|
14972
resources/avrdude/avrdude.conf
Normal file
BIN
resources/icons/Slic3r_192px_grayscale.png
Normal file
After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 616 B After Width: | Height: | Size: 510 B |
BIN
resources/icons/action_undo_grey.png
Normal file
After Width: | Height: | Size: 480 B |
BIN
resources/icons/add_object.png
Normal file
After Width: | Height: | Size: 829 B |
BIN
resources/icons/bed/mk2_bottom.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
resources/icons/bed/mk2_top.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
resources/icons/bed/mk3_bottom.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
resources/icons/bed/mk3_top.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
resources/icons/colorchange_add_off.png
Normal file
After Width: | Height: | Size: 600 B |
BIN
resources/icons/colorchange_add_on.png
Normal file
After Width: | Height: | Size: 695 B |
BIN
resources/icons/colorchange_delete_off.png
Normal file
After Width: | Height: | Size: 589 B |
BIN
resources/icons/colorchange_delete_on.png
Normal file
After Width: | Height: | Size: 628 B |
BIN
resources/icons/disclosure_triangle_close.png
Normal file
After Width: | Height: | Size: 212 B |
BIN
resources/icons/disclosure_triangle_open.png
Normal file
After Width: | Height: | Size: 210 B |
BIN
resources/icons/down_half_circle.png
Normal file
After Width: | Height: | Size: 631 B |
BIN
resources/icons/erase.png
Normal file
After Width: | Height: | Size: 488 B |
BIN
resources/icons/exclamation_mark_.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
resources/icons/lambda.png
Normal file
After Width: | Height: | Size: 913 B |
BIN
resources/icons/lambda_.png
Normal file
After Width: | Height: | Size: 422 B |
BIN
resources/icons/left_half_circle.png
Normal file
After Width: | Height: | Size: 651 B |
BIN
resources/icons/lock.png
Normal file
After Width: | Height: | Size: 749 B |
BIN
resources/icons/lock_open.png
Normal file
After Width: | Height: | Size: 727 B |
BIN
resources/icons/object.png
Normal file
After Width: | Height: | Size: 1017 B |
BIN
resources/icons/one_layer_lock_off.png
Normal file
After Width: | Height: | Size: 1001 B |
BIN
resources/icons/one_layer_lock_on.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/icons/one_layer_unlock_off.png
Normal file
After Width: | Height: | Size: 997 B |
BIN
resources/icons/one_layer_unlock_on.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/icons/overlay/layflat_hover.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icons/overlay/layflat_off.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
resources/icons/overlay/layflat_on.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
resources/icons/overlay/move_hover.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
resources/icons/overlay/move_off.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
resources/icons/overlay/move_on.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icons/overlay/rotate_hover.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
resources/icons/overlay/rotate_off.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
resources/icons/overlay/rotate_on.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
resources/icons/overlay/scale_hover.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
resources/icons/overlay/scale_off.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
resources/icons/overlay/scale_on.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
BIN
resources/icons/printers/PrusaResearch_MK2.5.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
resources/icons/printers/PrusaResearch_MK2S.png
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
resources/icons/printers/PrusaResearch_MK2SMM.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
resources/icons/printers/PrusaResearch_MK3.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
resources/icons/printers/PrusaResearch_MK3MMU2.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
resources/icons/question_mark_01.png
Normal file
After Width: | Height: | Size: 523 B |
BIN
resources/icons/right_half_circle.png
Normal file
After Width: | Height: | Size: 654 B |
BIN
resources/icons/split.png
Normal file
After Width: | Height: | Size: 1021 B |
BIN
resources/icons/sys_lock.png
Normal file
After Width: | Height: | Size: 510 B |
BIN
resources/icons/sys_unlock.png
Normal file
After Width: | Height: | Size: 419 B |
BIN
resources/icons/sys_unlock_grey.png
Normal file
After Width: | Height: | Size: 423 B |
BIN
resources/icons/toolbar.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
resources/icons/up_half_circle.png
Normal file
After Width: | Height: | Size: 650 B |
BIN
resources/localization/es/Slic3rPE.mo
Normal file
4473
resources/localization/es/Slic3rPE_es.po
Normal file
BIN
resources/localization/it/Slic3rPE.mo
Normal file
4480
resources/localization/it/Slic3rPE_it.po
Normal file
@ -1,16 +1,28 @@
|
||||
xs/src/slic3r/GUI/AboutDialog.cpp
|
||||
xs/src/slic3r/GUI/BedShapeDialog.cpp
|
||||
xs/src/slic3r/GUI/BedShapeDialog.hpp
|
||||
xs/src/slic3r/GUI/BonjourDialog.cpp
|
||||
xs/src/slic3r/GUI/ButtonsDescription.cpp
|
||||
xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp
|
||||
xs/src/slic3r/GUI/ConfigWizard.cpp
|
||||
xs/src/slic3r/GUI/FirmwareDialog.cpp
|
||||
xs/src/slic3r/GUI/GLCanvas3D.cpp
|
||||
xs/src/slic3r/GUI/GUI.cpp
|
||||
xs/src/slic3r/GUI/MsgDialog.cpp
|
||||
xs/src/slic3r/GUI/Tab.cpp
|
||||
xs/src/slic3r/GUI/Tab.hpp
|
||||
xs/src/slic3r/GUI/Field.cpp
|
||||
xs/src/slic3r/GUI/OptionsGroup.cpp
|
||||
xs/src/slic3r/GUI/2DBed.cpp
|
||||
xs/src/slic3r/GUI/Preset.cpp
|
||||
xs/src/slic3r/GUI/PresetBundle.cpp
|
||||
xs/src/slic3r/GUI/PresetHints.cpp
|
||||
xs/src/slic3r/GUI/Preferences.hpp
|
||||
xs/src/slic3r/GUI/Preferences.cpp
|
||||
xs/src/slic3r/GUI/BonjourDialog.cpp
|
||||
xs/src/slic3r/GUI/RammingChart.cpp
|
||||
xs/src/slic3r/GUI/UpdateDialogs.cpp
|
||||
xs/src/slic3r/GUI/WipeTowerDialog.cpp
|
||||
xs/src/slic3r/Utils/OctoPrint.cpp
|
||||
xs/src/slic3r/Utils/PresetUpdater.cpp
|
||||
xs/src/libslic3r/Print.cpp
|
||||
xs/src/libslic3r/PrintConfig.cpp
|
||||
xs/src/libslic3r/GCode/PreviewData.cpp
|
||||
lib/Slic3r/GUI.pm
|
||||
|