mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-12 09:17:52 -06:00
Merge branch 'master' into wipe_tower_improvements
This commit is contained in:
commit
1c6fa6660e
79 changed files with 34645 additions and 10116 deletions
|
@ -42,6 +42,21 @@ message("PATH: $ENV{PATH}")
|
||||||
message("PERL5LIB: $ENV{PERL5LIB}")
|
message("PERL5LIB: $ENV{PERL5LIB}")
|
||||||
find_package(Perl REQUIRED)
|
find_package(Perl REQUIRED)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
if(NOT DEFINED CMAKE_PREFIX_PATH)
|
||||||
|
if(DEFINED ENV{CMAKE_PREFIX_PATH})
|
||||||
|
set(CMAKE_PREFIX_PATH "$ENV{CMAKE_PREFIX_PATH}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# By default the startup project in MSVC is the 'ALL_BUILD' cmake-created project,
|
||||||
|
# but we want 'slic3r' as the startup one because debugging run command is associated with it.
|
||||||
|
# (Unfortunatelly it cannot be associated with ALL_BUILD using CMake.)
|
||||||
|
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT slic3r)
|
||||||
|
endif ()
|
||||||
|
|
||||||
add_subdirectory(xs)
|
add_subdirectory(xs)
|
||||||
|
|
||||||
enable_testing ()
|
enable_testing ()
|
||||||
|
|
59
cmake/modules/FindCURL.cmake
Normal file
59
cmake/modules/FindCURL.cmake
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||||
|
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||||
|
|
||||||
|
#.rst:
|
||||||
|
# FindCURL
|
||||||
|
# --------
|
||||||
|
#
|
||||||
|
# Find curl
|
||||||
|
#
|
||||||
|
# Find the native CURL headers and libraries.
|
||||||
|
#
|
||||||
|
# ::
|
||||||
|
#
|
||||||
|
# CURL_INCLUDE_DIRS - where to find curl/curl.h, etc.
|
||||||
|
# CURL_LIBRARIES - List of libraries when using curl.
|
||||||
|
# CURL_FOUND - True if curl found.
|
||||||
|
# CURL_VERSION_STRING - the version of curl found (since CMake 2.8.8)
|
||||||
|
|
||||||
|
# Look for the header file.
|
||||||
|
find_path(CURL_INCLUDE_DIR NAMES curl/curl.h)
|
||||||
|
mark_as_advanced(CURL_INCLUDE_DIR)
|
||||||
|
|
||||||
|
# Look for the library (sorted from most current/relevant entry to least).
|
||||||
|
find_library(CURL_LIBRARY NAMES
|
||||||
|
curl
|
||||||
|
# Windows MSVC Makefile:
|
||||||
|
libcurl_a
|
||||||
|
# Windows MSVC prebuilts:
|
||||||
|
curllib
|
||||||
|
libcurl_imp
|
||||||
|
curllib_static
|
||||||
|
# Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip):
|
||||||
|
libcurl
|
||||||
|
)
|
||||||
|
mark_as_advanced(CURL_LIBRARY)
|
||||||
|
|
||||||
|
if(CURL_INCLUDE_DIR)
|
||||||
|
foreach(_curl_version_header curlver.h curl.h)
|
||||||
|
if(EXISTS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}")
|
||||||
|
file(STRINGS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}" curl_version_str REGEX "^#define[\t ]+LIBCURL_VERSION[\t ]+\".*\"")
|
||||||
|
|
||||||
|
string(REGEX REPLACE "^#define[\t ]+LIBCURL_VERSION[\t ]+\"([^\"]*)\".*" "\\1" CURL_VERSION_STRING "${curl_version_str}")
|
||||||
|
unset(curl_version_str)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package_handle_standard_args(CURL
|
||||||
|
REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR
|
||||||
|
VERSION_VAR CURL_VERSION_STRING)
|
||||||
|
|
||||||
|
if(CURL_FOUND)
|
||||||
|
set(CURL_LIBRARIES ${CURL_LIBRARY})
|
||||||
|
set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
|
||||||
|
|
||||||
|
message(STATUS " Curl libraries: = ${CURL_LIBRARIES}")
|
||||||
|
message(STATUS " Curl include dirs: = ${CURL_INCLUDE_DIRS}")
|
||||||
|
endif()
|
9
cmake/msvc/slic3r.wperl64d.props
Normal file
9
cmake/msvc/slic3r.wperl64d.props
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<LocalDebuggerCommand>C:\wperl64d\bin\perl.exe</LocalDebuggerCommand>
|
||||||
|
<LocalDebuggerCommandArguments>slic3r.pl</LocalDebuggerCommandArguments>
|
||||||
|
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||||
|
<LocalDebuggerWorkingDirectory>..\..</LocalDebuggerWorkingDirectory>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
11
cmake/msvc/xs.wperl64d.props
Normal file
11
cmake/msvc/xs.wperl64d.props
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<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>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup />
|
||||||
|
<ItemGroup />
|
||||||
|
</Project>
|
2
doc/How to build - UNIX.md
Normal file
2
doc/How to build - UNIX.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Building Slic3r PE on Linux/UNIX
|
||||||
|
|
86
doc/How to build - Windows.md
Normal file
86
doc/How to build - Windows.md
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
# Building Slic3r PE on Microsoft Windows
|
||||||
|
|
||||||
|
The currently supported way of building Slic3r PE on Windows is with 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/).
|
||||||
|
|
||||||
|
Other setups (such as mingw + Strawberry Perl) _may_ work, but we cannot guarantee this will work
|
||||||
|
and cannot provide guidance.
|
||||||
|
|
||||||
|
|
||||||
|
### Geting the dependencies
|
||||||
|
|
||||||
|
First, download and upnack our Perl + wxWidgets binary distribution:
|
||||||
|
|
||||||
|
- 32 bit, release mode: [wperl32-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl32-5.24.0-2018-03-02.7z)
|
||||||
|
- 64 bit, release mode: [wperl64-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl64-5.24.0-2018-03-02.7z)
|
||||||
|
- 64 bit, release mode + debug symbols: [wperl64d-5.24.0-2018-03-02.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=wperl64d-5.24.0-2018-03-02.7z)
|
||||||
|
|
||||||
|
It is recommended to unpack this package into `C:\`.
|
||||||
|
|
||||||
|
Apart from wxWidgets and Perl, you will also need additional dependencies:
|
||||||
|
|
||||||
|
- Boost
|
||||||
|
- Intel TBB
|
||||||
|
- libcurl
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
It is recommended you unpack this package into `C:\local\` as the environment
|
||||||
|
setup script expects it there.
|
||||||
|
|
||||||
|
Alternatively you can also compile the additional dependencies yourself.
|
||||||
|
There is a [powershell script](./deps-build/windows/slic3r-makedeps.ps1) which automates this process.
|
||||||
|
|
||||||
|
### Building Slic3r PE
|
||||||
|
|
||||||
|
Once the dependencies are set up in their respective locations,
|
||||||
|
go to the `wperl*` directory extracted earlier and launch the `cmdline.lnk` file
|
||||||
|
which opens a command line prompt with appropriate environment variables set up.
|
||||||
|
|
||||||
|
In this command line, `cd` into the directory with Slic3r sources
|
||||||
|
and use these commands to build the Slic3r from the command line:
|
||||||
|
|
||||||
|
perl Build.PL
|
||||||
|
perl Build.PL --gui
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release
|
||||||
|
nmake
|
||||||
|
ctest --verbose # TODO: ???
|
||||||
|
cd ..
|
||||||
|
perl slic3r.pl
|
||||||
|
|
||||||
|
The above commands use `nmake` Makefiles.
|
||||||
|
You may also build Slic3r PE with other build tools:
|
||||||
|
|
||||||
|
|
||||||
|
### Building with Visual Studio
|
||||||
|
|
||||||
|
To build, lanuch and/or debug Slic3r PE with Visual Studio (64 bits), replace the `cmake` command with:
|
||||||
|
|
||||||
|
cmake .. -G "Visual Studio 12 Win64" -DCMAKE_CONFIGURATION_TYPES=Release;RelWithDebInfo || exit /b
|
||||||
|
|
||||||
|
For the 32-bit variant, use:
|
||||||
|
|
||||||
|
cmake .. -G "Visual Studio 12" -DCMAKE_CONFIGURATION_TYPES=Release;RelWithDebInfo || exit /b
|
||||||
|
|
||||||
|
After `cmake` has finished, go to the `Slic3r\build` directory and open the `Slic3r.sln` solution file.
|
||||||
|
This should open Visual Studio and load all the Slic3r solution containing all the projects.
|
||||||
|
Make sure you use Visual Studio 2013 to open the solution.
|
||||||
|
|
||||||
|
You can then use the usual Visual Studio controls to build Slic3r.
|
||||||
|
If you want to run or debug Slic3r from within Visual Studio, make sure the `slic3r` project is activated.
|
||||||
|
There are multiple projects in the Slic3r solution, but only the `slic3r` project is configured with the right
|
||||||
|
commands to run Slic3r.
|
||||||
|
|
||||||
|
|
||||||
|
### Building with ninja
|
||||||
|
|
||||||
|
To use [Ninja](TODO), replace the `cmake` and `nmake` commands with:
|
||||||
|
|
||||||
|
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||||
|
ninja
|
76
doc/Localization_guide.md
Normal file
76
doc/Localization_guide.md
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
# Localization and translation guide
|
||||||
|
|
||||||
|
The purpose of this guide is to describe how to contribute to the Slic3rPE translations. We use GNUgettext for extracting string resources from the project and PoEdit for editing translations.
|
||||||
|
|
||||||
|
Those are possible to download here:
|
||||||
|
- https://sourceforge.net/directory/os:windows/?q=gnu+gettext GNUgettext package contains a set of tools to extract strings from the source code and to create the translation Catalog.
|
||||||
|
- https://poedit.net PoEdit provides good interface for the translators.
|
||||||
|
|
||||||
|
After GNUgettext is installed it is recommended to add the path to gettext/bin to PATH variable.
|
||||||
|
|
||||||
|
Full manual for GNUgettext you can see here: http://www.gnu.org/software/gettext/manual/gettext.html
|
||||||
|
|
||||||
|
|
||||||
|
### Scenario 1. How do I add a translation or fix the existing translation
|
||||||
|
1. Get PO-file from corresponding folder here:
|
||||||
|
https://github.com/prusa3d/Slic3r/tree/master/resources/localization
|
||||||
|
2. Open this file in PoEdit as "Edit a translation"
|
||||||
|
3. Apply your corrections to translation
|
||||||
|
4. Push changed Slic3rPE.po and Slic3rPE.mo (will create automatically after saving of Slic3r.po in PoEdit) back to to the enter folder.
|
||||||
|
|
||||||
|
### Scenario 2. How do I add a new language support
|
||||||
|
1. Get file Slic3rPE.pot here :
|
||||||
|
https://github.com/prusa3d/Slic3r/tree/master/resources/localization
|
||||||
|
2. Open it in PoEdit for "Create new translation"
|
||||||
|
3. Select Translation Language (for example French).
|
||||||
|
4. As a result you will have fr.po - the file contaning translation to French.
|
||||||
|
Notice. When the transtation is complete you need to:
|
||||||
|
- Rename the file to Slic3rPE.po
|
||||||
|
- Click "Save file" button. Slic3rPE.mo will be created immediatly
|
||||||
|
- Both Slic3rPE.po and Slic3rPE.mo have to be saved here:
|
||||||
|
https://github.com/prusa3d/Slic3r/tree/master/resources/localization/fr
|
||||||
|
( name of folder "fr" means "French" - the translation language).
|
||||||
|
|
||||||
|
### Scenario 3. How do I add a new text resource when implementing a feature to Slic3rPE
|
||||||
|
Each string resource in Slic3rPE available for translation needs to be explicitly marked using L() macro like this:
|
||||||
|
```C++
|
||||||
|
auto msg = L("This message to be localized")
|
||||||
|
```
|
||||||
|
To get translated text use one of needed macro/function (`_(s)`, `_CHB(s)` or `L_str(s)` ).
|
||||||
|
If you add new file resourse, add it to list of files contaned macro `L()`
|
||||||
|
|
||||||
|
### Scenario 4. How do I use GNUgettext to localize my own application taking Slic3rPE as an example
|
||||||
|
|
||||||
|
1. For conviniance create list of files with this macro `L(s)`. We have
|
||||||
|
https://github.com/prusa3d/Slic3r/tree/master/resources/localization/list.txt.
|
||||||
|
|
||||||
|
2. Create template file(*.POT) with GNUgettext command:
|
||||||
|
```
|
||||||
|
xgettext --keyword=L --from-code=UTF-8 --debug -o Slic3rPE.pot -f list.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Use flag `--from-code=UTF-8` to specify that the source strings are in UTF-8 encoding
|
||||||
|
Use flag `--debug` to correctly extract formated strings(used %d, %s etc.)
|
||||||
|
|
||||||
|
3. Create PO- and MO-files for your project as described above.
|
||||||
|
|
||||||
|
4. To merge old PO-file with strings from creaded new POT-file use command:
|
||||||
|
```
|
||||||
|
msgmerge -N -o new.po old.po new.pot
|
||||||
|
```
|
||||||
|
Use option `-N` to not using fuzzy matching when an exact match is not found.
|
||||||
|
|
||||||
|
5. To concatenate old PO-file with strings from new PO-file use command:
|
||||||
|
```
|
||||||
|
msgcat -o new.po old.po
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Create an English translation catalog with command:
|
||||||
|
```
|
||||||
|
msgen -o new.po old.po
|
||||||
|
```
|
||||||
|
Notice, in this Catalog it will be totally same strings for initial text and translated.
|
||||||
|
|
||||||
|
When you have Catalog to translation open POT or PO file in PoEdit and start to translation.
|
||||||
|
It's very important to keep attention to every gaps and punctuation. Especially with
|
||||||
|
formated strings. (used %d, %s etc.)
|
132
doc/deps-build/unix-static/Makefile
Normal file
132
doc/deps-build/unix-static/Makefile
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
|
||||||
|
#
|
||||||
|
# This makefile downloads, configures and builds Slic3r PE dependencies for Unix.
|
||||||
|
# (That is, all dependencies except perl + wxWidgets.)
|
||||||
|
# The libraries are installed in DESTDIR, which you can customize like so:
|
||||||
|
#
|
||||||
|
# DESTDIR=foo/bar make
|
||||||
|
#
|
||||||
|
# The default DESTDIR is ~/slic3r-destdir
|
||||||
|
# If the DESTDIR doesn't exits, the makefile tries to create it
|
||||||
|
#
|
||||||
|
# To pass the DESTDIR path along to cmake, set the use CMAKE_PREFIX_PATH variable
|
||||||
|
# and set it to $DESTDIR/usr/local
|
||||||
|
#
|
||||||
|
# You can also customize the NPROC variable in the same way to configure the number
|
||||||
|
# of cores the build process uses. By default this is set to what the `nproc` command says.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
DESTDIR ?= $(HOME)/slic3r-destdir
|
||||||
|
NPROC ?= $(shell nproc)
|
||||||
|
|
||||||
|
|
||||||
|
BOOST = boost_1_66_0
|
||||||
|
TBB_SHA = a0dc9bf76d0120f917b641ed095360448cabc85b
|
||||||
|
TBB = tbb-$(TBB_SHA)
|
||||||
|
OPENSSL = openssl-OpenSSL_1_1_0g
|
||||||
|
CURL = curl-7.58.0
|
||||||
|
|
||||||
|
.PHONY: all destdir boost libcurl libopenssl libtbb
|
||||||
|
|
||||||
|
all: destdir boost libtbb libcurl
|
||||||
|
@echo
|
||||||
|
@echo "All done!"
|
||||||
|
@echo
|
||||||
|
|
||||||
|
destdir:
|
||||||
|
mkdir -p $(DESTDIR)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
boost: $(BOOST).tar.gz
|
||||||
|
tar -zxvf $(BOOST).tar.gz
|
||||||
|
cd $(BOOST) && ./bootstrap.sh --with-libraries=system,filesystem,thread,log,locale,regex --prefix=$(DESTDIR)/usr/local
|
||||||
|
cd $(BOOST) && ./b2 \
|
||||||
|
-j $(NPROC) \
|
||||||
|
link=static \
|
||||||
|
variant=release \
|
||||||
|
threading=multi \
|
||||||
|
boost.locale.icu=off \
|
||||||
|
cxxflags=-fPIC cflags=-fPIC \
|
||||||
|
install
|
||||||
|
|
||||||
|
$(BOOST).tar.gz:
|
||||||
|
curl -L -o $@ https://dl.bintray.com/boostorg/release/1.66.0/source/$@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
libtbb: $(TBB).tar.gz
|
||||||
|
tar -zxvf $(TBB).tar.gz
|
||||||
|
mkdir -p $(TBB)/mybuild
|
||||||
|
cd $(TBB)/mybuild && cmake .. -DTBB_BUILD_SHARED=OFF -DTBB_BUILD_TESTS=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||||
|
$(MAKE) -C $(TBB)/mybuild -j$(NPROC)
|
||||||
|
$(MAKE) -C $(TBB)/mybuild install DESTDIR=$(DESTDIR)
|
||||||
|
|
||||||
|
$(TBB).tar.gz:
|
||||||
|
curl -L -o $@ https://github.com/wjakob/tbb/archive/$(TBB_SHA).tar.gz
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
libopenssl: $(OPENSSL).tar.gz
|
||||||
|
tar -zxvf $(OPENSSL).tar.gz
|
||||||
|
cd $(OPENSSL) && ./config --openssldir=/etc/ssl shared no-ssl3-method no-dynamic-engine '-Wa,--noexecstack'
|
||||||
|
make -C $(OPENSSL) depend
|
||||||
|
make -C $(OPENSSL) -j$(NPROC)
|
||||||
|
make -C $(OPENSSL) install DESTDIR=$(DESTDIR)
|
||||||
|
|
||||||
|
$(OPENSSL).tar.gz:
|
||||||
|
curl -L -o $@ 'https://github.com/openssl/openssl/archive/OpenSSL_1_1_0g.tar.gz'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
libcurl: libopenssl $(CURL).tar.gz
|
||||||
|
tar -zxvf $(CURL).tar.gz
|
||||||
|
# XXX: disable shared?
|
||||||
|
# Setting PKG_CONFIG_PATH should make libcurl find our previously built openssl
|
||||||
|
cd $(CURL) && PKG_CONFIG_PATH=$(DESTDIR)/usr/local/lib/pkgconfig ./configure \
|
||||||
|
--enable-static \
|
||||||
|
--enable-shared \
|
||||||
|
--with-pic \
|
||||||
|
--enable-ipv6 \
|
||||||
|
--enable-versioned-symbols \
|
||||||
|
--enable-threaded-resolver \
|
||||||
|
--with-random=/dev/urandom \
|
||||||
|
--with-ca-bundle=/etc/ssl/certs/ca-certificates.crt \
|
||||||
|
--disable-ldap \
|
||||||
|
--disable-ldaps \
|
||||||
|
--disable-manual \
|
||||||
|
--disable-rtsp \
|
||||||
|
--disable-dict \
|
||||||
|
--disable-telnet \
|
||||||
|
--disable-pop3 \
|
||||||
|
--disable-imap \
|
||||||
|
--disable-smb \
|
||||||
|
--disable-smtp \
|
||||||
|
--disable-gopher \
|
||||||
|
--disable-crypto-auth \
|
||||||
|
--without-gssapi \
|
||||||
|
--without-libpsl \
|
||||||
|
--without-libidn2 \
|
||||||
|
--without-gnutls \
|
||||||
|
--without-polarssl \
|
||||||
|
--without-mbedtls \
|
||||||
|
--without-cyassl \
|
||||||
|
--without-nss \
|
||||||
|
--without-axtls \
|
||||||
|
--without-brotli \
|
||||||
|
--without-libmetalink \
|
||||||
|
--without-libssh \
|
||||||
|
--without-libssh2 \
|
||||||
|
--without-librtmp \
|
||||||
|
--without-nghttp2 \
|
||||||
|
--without-zsh-functions-dir
|
||||||
|
$(MAKE) -C $(CURL) -j$(NPROC)
|
||||||
|
$(MAKE) -C $(CURL) install DESTDIR=$(DESTDIR)
|
||||||
|
|
||||||
|
$(CURL).tar.gz:
|
||||||
|
curl -L -o $@ https://curl.haxx.se/download/$@
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(BOOST) $(BOOST).tar.gz $(TBB) $(TBB).tar.gz $(OPENSSL) $(OPENSSL).tar.gz $(CURL) $(CURL).tar.gz
|
141
doc/deps-build/windows/slic3r-makedeps.ps1
Normal file
141
doc/deps-build/windows/slic3r-makedeps.ps1
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
#!powershell
|
||||||
|
#
|
||||||
|
# This script downloads, configures and builds Slic3r PE dependencies for Unix.
|
||||||
|
# (That is, all dependencies except perl + wxWidgets.)
|
||||||
|
#
|
||||||
|
# To use this script, launch the Visual Studio command line,
|
||||||
|
# `cd` into the directory containing this script and use this command:
|
||||||
|
#
|
||||||
|
# powershell .\slic3r-makedeps.ps1
|
||||||
|
#
|
||||||
|
# The dependencies will be downloaded and unpacked into the current dir.
|
||||||
|
# This script WILL NOT try to guess the build architecture (64 vs 32 bits),
|
||||||
|
# it will by default build the 64-bit variant. To build the 32-bit variant, use:
|
||||||
|
#
|
||||||
|
# powershell .\slic3r-makedeps.ps1 -b32
|
||||||
|
#
|
||||||
|
# Built libraries are installed into $destdir,
|
||||||
|
# which by default is C:\local\slic3r-destdir-$bits
|
||||||
|
# You can customize the $destdir using:
|
||||||
|
#
|
||||||
|
# powershell .\slic3r-makedeps.ps1 -destdir C:\foo\bar
|
||||||
|
#
|
||||||
|
# To pass the $destdir path along to cmake, set the use CMAKE_PREFIX_PATH variable
|
||||||
|
# and set it to $destdir\usr\local
|
||||||
|
#
|
||||||
|
# Script requirements: PowerShell 3.0, .NET 4.5
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
param(
|
||||||
|
[switch]$b32 = $false,
|
||||||
|
[string]$destdir = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($destdir -eq "") {
|
||||||
|
$destdir = "C:\local\slic3r-destdir-" + ('32', '64')[!$b32]
|
||||||
|
}
|
||||||
|
|
||||||
|
$BOOST = 'boost_1_63_0'
|
||||||
|
$CURL = 'curl-7.28.0'
|
||||||
|
$TBB_SHA = 'a0dc9bf76d0120f917b641ed095360448cabc85b'
|
||||||
|
$TBB = "tbb-$TBB_SHA"
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
# Set up various settings and utilities:
|
||||||
|
[Environment]::CurrentDirectory = Get-Location
|
||||||
|
$NPROC = (Get-WmiObject -class Win32_processor).NumberOfLogicalProcessors
|
||||||
|
Add-Type -A System.IO.Compression.FileSystem
|
||||||
|
# This fxies SSL/TLS errors, credit goes to Ansible; see their `win_get_url.ps1` file
|
||||||
|
$security_protcols = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::SystemDefault
|
||||||
|
if ([Net.SecurityProtocolType].GetMember('Tls11').Count -gt 0) {
|
||||||
|
$security_protcols = $security_protcols -bor [Net.SecurityProtocolType]::Tls11
|
||||||
|
}
|
||||||
|
if ([Net.SecurityProtocolType].GetMember('Tls12').Count -gt 0) {
|
||||||
|
$security_protcols = $security_protcols -bor [Net.SecurityProtocolType]::Tls12
|
||||||
|
}
|
||||||
|
[Net.ServicePointManager]::SecurityProtocol = $security_protcols
|
||||||
|
$webclient = New-Object System.Net.WebClient
|
||||||
|
|
||||||
|
|
||||||
|
# Ensure DESTDIR exists:
|
||||||
|
mkdir $destdir -ea 0
|
||||||
|
mkdir "$destdir\usr\local" -ea 0
|
||||||
|
|
||||||
|
|
||||||
|
# Download sources:
|
||||||
|
echo 'Downloading sources ...'
|
||||||
|
if (!(Test-Path "$BOOST.zip")) { $webclient.DownloadFile("https://dl.bintray.com/boostorg/release/1.63.0/source/$BOOST.zip", "$BOOST.zip") }
|
||||||
|
if (!(Test-Path "$TBB.zip")) { $webclient.DownloadFile("https://github.com/wjakob/tbb/archive/$TBB_SHA.zip", "$TBB.zip") }
|
||||||
|
if (!(Test-Path "$CURL.zip")) { $webclient.DownloadFile("https://curl.haxx.se/download/$CURL.zip", ".\$CURL.zip") }
|
||||||
|
|
||||||
|
|
||||||
|
# Unpack sources:
|
||||||
|
echo 'Unpacking ...'
|
||||||
|
if (!(Test-Path $BOOST)) { [IO.Compression.ZipFile]::ExtractToDirectory("$BOOST.zip", '.') }
|
||||||
|
if (!(Test-Path $TBB)) { [IO.Compression.ZipFile]::ExtractToDirectory("$TBB.zip", '.') }
|
||||||
|
if (!(Test-Path $CURL)) { [IO.Compression.ZipFile]::ExtractToDirectory("$CURL.zip", '.') }
|
||||||
|
|
||||||
|
|
||||||
|
# Build libraries:
|
||||||
|
echo 'Building ...'
|
||||||
|
|
||||||
|
# Build boost
|
||||||
|
pushd "$BOOST"
|
||||||
|
.\bootstrap
|
||||||
|
$adr_mode = ('32', '64')[!$b32]
|
||||||
|
.\b2 `
|
||||||
|
-j "$NPROC" `
|
||||||
|
--with-system `
|
||||||
|
--with-filesystem `
|
||||||
|
--with-thread `
|
||||||
|
--with-log `
|
||||||
|
--with-locale `
|
||||||
|
--with-regex `
|
||||||
|
"--prefix=$destdir/usr/local" `
|
||||||
|
"address-model=$adr_mode" `
|
||||||
|
toolset=msvc-12.0 `
|
||||||
|
link=static `
|
||||||
|
variant=release `
|
||||||
|
threading=multi `
|
||||||
|
boost.locale.icu=off `
|
||||||
|
install
|
||||||
|
popd
|
||||||
|
|
||||||
|
# Build TBB
|
||||||
|
pushd "$TBB"
|
||||||
|
mkdir 'mybuild' -ea 0
|
||||||
|
cd 'mybuild'
|
||||||
|
$generator = ('Visual Studio 12', 'Visual Studio 12 Win64')[!$b32]
|
||||||
|
cmake .. `
|
||||||
|
-G "$generator" `
|
||||||
|
-DCMAKE_CONFIGURATION_TYPES=Release `
|
||||||
|
-DTBB_BUILD_SHARED=OFF `
|
||||||
|
-DTBB_BUILD_TESTS=OFF "-DCMAKE_INSTALL_PREFIX:PATH=$destdir\usr\local"
|
||||||
|
msbuild /P:Configuration=Release INSTALL.vcxproj
|
||||||
|
popd
|
||||||
|
|
||||||
|
# Build libcurl:
|
||||||
|
pushd "$CURL\winbuild"
|
||||||
|
$machine = ("x86", "x64")[!$b32]
|
||||||
|
nmake /f Makefile.vc mode=static VC=12 GEN_PDB=yes DEBUG=no "MACHINE=$machine"
|
||||||
|
Copy-Item -R -Force ..\builds\libcurl-*-winssl\include\* "$destdir\usr\local\include\"
|
||||||
|
Copy-Item -R -Force ..\builds\libcurl-*-winssl\lib\* "$destdir\usr\local\lib\"
|
||||||
|
popd
|
||||||
|
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "All done!"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
catch [Exception]
|
||||||
|
{
|
||||||
|
# This prints errors in a verbose manner
|
||||||
|
echo $_.Exception|format-list -force
|
||||||
|
}
|
|
@ -26,12 +26,13 @@ use Slic3r::GUI::Plater::ObjectCutDialog;
|
||||||
use Slic3r::GUI::Plater::ObjectSettingsDialog;
|
use Slic3r::GUI::Plater::ObjectSettingsDialog;
|
||||||
use Slic3r::GUI::Plater::LambdaObjectDialog;
|
use Slic3r::GUI::Plater::LambdaObjectDialog;
|
||||||
use Slic3r::GUI::Plater::OverrideSettingsPanel;
|
use Slic3r::GUI::Plater::OverrideSettingsPanel;
|
||||||
use Slic3r::GUI::Preferences;
|
|
||||||
use Slic3r::GUI::ProgressStatusBar;
|
use Slic3r::GUI::ProgressStatusBar;
|
||||||
use Slic3r::GUI::OptionsGroup;
|
use Slic3r::GUI::OptionsGroup;
|
||||||
use Slic3r::GUI::OptionsGroup::Field;
|
use Slic3r::GUI::OptionsGroup::Field;
|
||||||
use Slic3r::GUI::SystemInfo;
|
use Slic3r::GUI::SystemInfo;
|
||||||
|
|
||||||
|
use Wx::Locale gettext => 'L';
|
||||||
|
|
||||||
our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1";
|
our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1";
|
||||||
our $have_LWP = eval "use LWP::UserAgent; 1";
|
our $have_LWP = eval "use LWP::UserAgent; 1";
|
||||||
|
|
||||||
|
@ -40,10 +41,10 @@ use Wx::Event qw(EVT_IDLE EVT_COMMAND EVT_MENU);
|
||||||
use base 'Wx::App';
|
use base 'Wx::App';
|
||||||
|
|
||||||
use constant FILE_WILDCARDS => {
|
use constant FILE_WILDCARDS => {
|
||||||
known => 'Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA',
|
known => 'Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.zip.amf;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA',
|
||||||
stl => 'STL files (*.stl)|*.stl;*.STL',
|
stl => 'STL files (*.stl)|*.stl;*.STL',
|
||||||
obj => 'OBJ files (*.obj)|*.obj;*.OBJ',
|
obj => 'OBJ files (*.obj)|*.obj;*.OBJ',
|
||||||
amf => 'AMF files (*.amf)|*.amf;*.AMF;*.xml;*.XML',
|
amf => 'AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML',
|
||||||
threemf => '3MF files (*.3mf)|*.3mf;*.3MF',
|
threemf => '3MF files (*.3mf)|*.3mf;*.3MF',
|
||||||
prusa => 'Prusa Control files (*.prusa)|*.prusa;*.PRUSA',
|
prusa => 'Prusa Control files (*.prusa)|*.prusa;*.PRUSA',
|
||||||
ini => 'INI files *.ini|*.ini;*.INI',
|
ini => 'INI files *.ini|*.ini;*.INI',
|
||||||
|
@ -69,7 +70,9 @@ our $grey = Wx::Colour->new(200,200,200);
|
||||||
|
|
||||||
# Events to be sent from a C++ menu implementation:
|
# Events to be sent from a C++ menu implementation:
|
||||||
# 1) To inform about a change of the application language.
|
# 1) To inform about a change of the application language.
|
||||||
our $LANGUAGE_CHANGE_EVENT = Wx::NewEventType;
|
our $LANGUAGE_CHANGE_EVENT = Wx::NewEventType;
|
||||||
|
# 2) To inform about a change of Preferences.
|
||||||
|
our $PREFERENCES_EVENT = Wx::NewEventType;
|
||||||
|
|
||||||
sub OnInit {
|
sub OnInit {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
@ -84,12 +87,11 @@ sub OnInit {
|
||||||
# Mac: "~/Library/Application Support/Slic3r"
|
# Mac: "~/Library/Application Support/Slic3r"
|
||||||
Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir);
|
Slic3r::set_data_dir($datadir || Wx::StandardPaths::Get->GetUserDataDir);
|
||||||
Slic3r::GUI::set_wxapp($self);
|
Slic3r::GUI::set_wxapp($self);
|
||||||
Slic3r::GUI::load_language();
|
|
||||||
|
|
||||||
$self->{notifier} = Slic3r::GUI::Notifier->new;
|
$self->{notifier} = Slic3r::GUI::Notifier->new;
|
||||||
$self->{app_config} = Slic3r::GUI::AppConfig->new;
|
$self->{app_config} = Slic3r::GUI::AppConfig->new;
|
||||||
$self->{preset_bundle} = Slic3r::GUI::PresetBundle->new;
|
$self->{preset_bundle} = Slic3r::GUI::PresetBundle->new;
|
||||||
|
|
||||||
# just checking for existence of Slic3r::data_dir is not enough: it may be an empty directory
|
# 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
|
# supplied as argument to --datadir; in that case we should still run the wizard
|
||||||
eval { $self->{preset_bundle}->setup_directories() };
|
eval { $self->{preset_bundle}->setup_directories() };
|
||||||
|
@ -103,6 +105,9 @@ sub OnInit {
|
||||||
$self->{app_config}->set('version', $Slic3r::VERSION);
|
$self->{app_config}->set('version', $Slic3r::VERSION);
|
||||||
$self->{app_config}->save;
|
$self->{app_config}->save;
|
||||||
|
|
||||||
|
Slic3r::GUI::set_app_config($self->{app_config});
|
||||||
|
Slic3r::GUI::load_language();
|
||||||
|
|
||||||
# Suppress the '- default -' presets.
|
# Suppress the '- default -' presets.
|
||||||
$self->{preset_bundle}->set_default_suppressed($self->{app_config}->get('no_defaults') ? 1 : 0);
|
$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 };
|
||||||
|
@ -120,6 +125,7 @@ sub OnInit {
|
||||||
no_controller => $self->{app_config}->get('no_controller'),
|
no_controller => $self->{app_config}->get('no_controller'),
|
||||||
no_plater => $no_plater,
|
no_plater => $no_plater,
|
||||||
lang_ch_event => $LANGUAGE_CHANGE_EVENT,
|
lang_ch_event => $LANGUAGE_CHANGE_EVENT,
|
||||||
|
preferences_event => $PREFERENCES_EVENT,
|
||||||
);
|
);
|
||||||
$self->SetTopWindow($frame);
|
$self->SetTopWindow($frame);
|
||||||
|
|
||||||
|
@ -144,6 +150,11 @@ sub OnInit {
|
||||||
EVT_COMMAND($self, -1, $LANGUAGE_CHANGE_EVENT, sub{
|
EVT_COMMAND($self, -1, $LANGUAGE_CHANGE_EVENT, sub{
|
||||||
$self->recreate_GUI;
|
$self->recreate_GUI;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
# The following event is emited by the C++ menu implementation of preferences change.
|
||||||
|
EVT_COMMAND($self, -1, $PREFERENCES_EVENT, sub{
|
||||||
|
$self->update_ui_from_settings;
|
||||||
|
});
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -156,6 +167,7 @@ sub recreate_GUI{
|
||||||
no_controller => $self->{app_config}->get('no_controller'),
|
no_controller => $self->{app_config}->get('no_controller'),
|
||||||
no_plater => $no_plater,
|
no_plater => $no_plater,
|
||||||
lang_ch_event => $LANGUAGE_CHANGE_EVENT,
|
lang_ch_event => $LANGUAGE_CHANGE_EVENT,
|
||||||
|
preferences_event => $PREFERENCES_EVENT,
|
||||||
);
|
);
|
||||||
|
|
||||||
if($topwindow)
|
if($topwindow)
|
||||||
|
@ -270,8 +282,9 @@ sub update_ui_from_settings {
|
||||||
|
|
||||||
sub open_model {
|
sub open_model {
|
||||||
my ($self, $window) = @_;
|
my ($self, $window) = @_;
|
||||||
|
|
||||||
my $dialog = Wx::FileDialog->new($window // $self->GetTopWindow, 'Choose one or more files (STL/OBJ/AMF/PRUSA):',
|
my $dlg_title = L('Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):');
|
||||||
|
my $dialog = Wx::FileDialog->new($window // $self->GetTopWindow, $dlg_title,
|
||||||
$self->{app_config}->get_last_dir, "",
|
$self->{app_config}->get_last_dir, "",
|
||||||
MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
|
MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
|
||||||
if ($dialog->ShowModal != wxID_OK) {
|
if ($dialog->ShowModal != wxID_OK) {
|
||||||
|
|
|
@ -1172,18 +1172,17 @@ sub Render {
|
||||||
glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0);
|
glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0);
|
||||||
|
|
||||||
if ($self->enable_picking) {
|
if ($self->enable_picking) {
|
||||||
# Render the object for picking.
|
|
||||||
# FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing.
|
|
||||||
# Better to use software ray-casting on a bounding-box hierarchy.
|
|
||||||
glPushAttrib(GL_ENABLE_BIT);
|
|
||||||
glDisable(GL_MULTISAMPLE) if ($self->{can_multisample});
|
|
||||||
glDisable(GL_LIGHTING);
|
|
||||||
glDisable(GL_BLEND);
|
|
||||||
$self->draw_volumes(1);
|
|
||||||
glFlush();
|
|
||||||
glFinish();
|
|
||||||
|
|
||||||
if (my $pos = $self->_mouse_pos) {
|
if (my $pos = $self->_mouse_pos) {
|
||||||
|
# Render the object for picking.
|
||||||
|
# FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing.
|
||||||
|
# Better to use software ray-casting on a bounding-box hierarchy.
|
||||||
|
glPushAttrib(GL_ENABLE_BIT);
|
||||||
|
glDisable(GL_MULTISAMPLE) if ($self->{can_multisample});
|
||||||
|
glDisable(GL_LIGHTING);
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
$self->draw_volumes(1);
|
||||||
|
glPopAttrib();
|
||||||
|
glFlush();
|
||||||
my $col = [ glReadPixels_p($pos->x, $self->GetSize->GetHeight - $pos->y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE) ];
|
my $col = [ glReadPixels_p($pos->x, $self->GetSize->GetHeight - $pos->y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE) ];
|
||||||
my $volume_idx = $col->[0] + $col->[1]*256 + $col->[2]*256*256;
|
my $volume_idx = $col->[0] + $col->[1]*256 + $col->[2]*256*256;
|
||||||
$self->_hover_volume_idx(undef);
|
$self->_hover_volume_idx(undef);
|
||||||
|
@ -1199,11 +1198,8 @@ sub Render {
|
||||||
|
|
||||||
$self->on_hover->($volume_idx) if $self->on_hover;
|
$self->on_hover->($volume_idx) if $self->on_hover;
|
||||||
}
|
}
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
glFlush();
|
|
||||||
glFinish();
|
|
||||||
glPopAttrib();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# draw fixed background
|
# draw fixed background
|
||||||
|
@ -1337,9 +1333,6 @@ sub Render {
|
||||||
$self->draw_active_object_annotations;
|
$self->draw_active_object_annotations;
|
||||||
|
|
||||||
$self->SwapBuffers();
|
$self->SwapBuffers();
|
||||||
|
|
||||||
# Calling glFinish has a performance penalty, but it seems to fix some OpenGL driver hang-up with extremely large scenes.
|
|
||||||
# glFinish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub draw_volumes {
|
sub draw_volumes {
|
||||||
|
@ -2051,4 +2044,11 @@ sub reset_legend_texture {
|
||||||
Slic3r::GUI::_3DScene::reset_legend_texture();
|
Slic3r::GUI::_3DScene::reset_legend_texture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub get_current_print_zs {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
my $count = $self->volumes->get_current_print_zs();
|
||||||
|
return $count;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -14,6 +14,8 @@ use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedi
|
||||||
use Wx::Event qw(EVT_CLOSE EVT_COMMAND EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED);
|
use Wx::Event qw(EVT_CLOSE EVT_COMMAND EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED);
|
||||||
use base 'Wx::Frame';
|
use base 'Wx::Frame';
|
||||||
|
|
||||||
|
use Wx::Locale gettext => 'L';
|
||||||
|
|
||||||
our $qs_last_input_file;
|
our $qs_last_input_file;
|
||||||
our $qs_last_output_file;
|
our $qs_last_output_file;
|
||||||
our $last_config;
|
our $last_config;
|
||||||
|
@ -48,7 +50,8 @@ sub new {
|
||||||
$self->{no_plater} = $params{no_plater};
|
$self->{no_plater} = $params{no_plater};
|
||||||
$self->{loaded} = 0;
|
$self->{loaded} = 0;
|
||||||
$self->{lang_ch_event} = $params{lang_ch_event};
|
$self->{lang_ch_event} = $params{lang_ch_event};
|
||||||
|
$self->{preferences_event} = $params{preferences_event};
|
||||||
|
|
||||||
# initialize tabpanel and menubar
|
# initialize tabpanel and menubar
|
||||||
$self->_init_tabpanel;
|
$self->_init_tabpanel;
|
||||||
$self->_init_menubar;
|
$self->_init_menubar;
|
||||||
|
@ -60,7 +63,7 @@ sub new {
|
||||||
|
|
||||||
# initialize status bar
|
# initialize status bar
|
||||||
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1);
|
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1);
|
||||||
$self->{statusbar}->SetStatusText("Version $Slic3r::VERSION - Remember to check for updates at http://github.com/prusa3d/slic3r/releases");
|
$self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
|
||||||
$self->SetStatusBar($self->{statusbar});
|
$self->SetStatusBar($self->{statusbar});
|
||||||
|
|
||||||
$self->{loaded} = 1;
|
$self->{loaded} = 1;
|
||||||
|
@ -112,9 +115,9 @@ sub _init_tabpanel {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!$self->{no_plater}) {
|
if (!$self->{no_plater}) {
|
||||||
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "Plater");
|
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), L("Plater"));
|
||||||
if (!$self->{no_controller}) {
|
if (!$self->{no_controller}) {
|
||||||
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
|
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), L("Controller"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +174,6 @@ sub _init_tabpanel {
|
||||||
EVT_COMMAND($self, -1, $BUTTON_BROWSE_EVENT, sub {
|
EVT_COMMAND($self, -1, $BUTTON_BROWSE_EVENT, sub {
|
||||||
my ($self, $event) = @_;
|
my ($self, $event) = @_;
|
||||||
my $msg = $event->GetString;
|
my $msg = $event->GetString;
|
||||||
print "BUTTON_BROWSE_EVENT: ", $msg, "\n";
|
|
||||||
|
|
||||||
# look for devices
|
# look for devices
|
||||||
my $entries;
|
my $entries;
|
||||||
|
@ -186,7 +188,7 @@ sub _init_tabpanel {
|
||||||
$tab->load_key_value('octoprint_host', $dlg->GetValue . ":" . $dlg->GetPort)
|
$tab->load_key_value('octoprint_host', $dlg->GetValue . ":" . $dlg->GetPort)
|
||||||
if $dlg->ShowModal == wxID_OK;
|
if $dlg->ShowModal == wxID_OK;
|
||||||
} else {
|
} else {
|
||||||
Wx::MessageDialog->new($self, 'No Bonjour device found', 'Device Browser', wxOK | wxICON_INFORMATION)->ShowModal;
|
Wx::MessageDialog->new($self, L('No Bonjour device found'), L('Device Browser'), wxOK | wxICON_INFORMATION)->ShowModal;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
# The following event is emited by the C++ Tab implementation ,
|
# The following event is emited by the C++ Tab implementation ,
|
||||||
|
@ -194,7 +196,6 @@ sub _init_tabpanel {
|
||||||
EVT_COMMAND($self, -1, $BUTTON_TEST_EVENT, sub {
|
EVT_COMMAND($self, -1, $BUTTON_TEST_EVENT, sub {
|
||||||
my ($self, $event) = @_;
|
my ($self, $event) = @_;
|
||||||
my $msg = $event->GetString;
|
my $msg = $event->GetString;
|
||||||
print "BUTTON_TEST_EVENT: ", $msg, "\n";
|
|
||||||
|
|
||||||
my $ua = LWP::UserAgent->new;
|
my $ua = LWP::UserAgent->new;
|
||||||
$ua->timeout(10);
|
$ua->timeout(10);
|
||||||
|
@ -205,19 +206,19 @@ sub _init_tabpanel {
|
||||||
'X-Api-Key' => $config->octoprint_apikey,
|
'X-Api-Key' => $config->octoprint_apikey,
|
||||||
);
|
);
|
||||||
if ($res->is_success) {
|
if ($res->is_success) {
|
||||||
Slic3r::GUI::show_info($self, "Connection to OctoPrint works correctly.", "Success!");
|
Slic3r::GUI::show_info($self, L("Connection to OctoPrint works correctly."), _L("Success!"));
|
||||||
} else {
|
} else {
|
||||||
Slic3r::GUI::show_error($self,
|
Slic3r::GUI::show_error($self,
|
||||||
"I wasn't able to connect to OctoPrint (" . $res->status_line . "). "
|
L("I wasn't able to connect to OctoPrint (") . $res->status_line .
|
||||||
. "Check hostname and OctoPrint version (at least 1.1.0 is required).");
|
L("). Check hostname and OctoPrint version (at least 1.1.0 is required)."));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
# A variable to inform C++ Tab implementation about disabling of Browse button
|
# A variable to inform C++ Tab implementation about disabling of Browse button
|
||||||
$self->{is_disabled_button_browse} = (!eval "use Net::Bonjour; 1") ? 1 : 0 ;
|
$self->{is_disabled_button_browse} = (!eval "use Net::Bonjour; 1") ? 1 : 0 ;
|
||||||
# A variable to inform C++ Tab implementation about user_agent
|
# A variable to inform C++ Tab implementation about user_agent
|
||||||
$self->{is_user_agent} = (eval "use LWP::UserAgent; 1") ? 1 : 0 ;
|
$self->{is_user_agent} = (eval "use LWP::UserAgent; 1") ? 1 : 0 ;
|
||||||
Slic3r::GUI::create_preset_tabs(wxTheApp->{preset_bundle}, wxTheApp->{app_config},
|
Slic3r::GUI::create_preset_tabs(wxTheApp->{preset_bundle}, $self->{no_controller},
|
||||||
$self->{no_controller}, $self->{is_disabled_button_browse},
|
$self->{is_disabled_button_browse},
|
||||||
$self->{is_user_agent},
|
$self->{is_user_agent},
|
||||||
$VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT,
|
$VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT,
|
||||||
$BUTTON_BROWSE_EVENT, $BUTTON_TEST_EVENT);
|
$BUTTON_BROWSE_EVENT, $BUTTON_TEST_EVENT);
|
||||||
|
@ -245,59 +246,60 @@ sub _init_menubar {
|
||||||
# File menu
|
# File menu
|
||||||
my $fileMenu = Wx::Menu->new;
|
my $fileMenu = Wx::Menu->new;
|
||||||
{
|
{
|
||||||
wxTheApp->append_menu_item($fileMenu, "Open STL/OBJ/AMF…\tCtrl+O", 'Open a model', sub {
|
wxTheApp->append_menu_item($fileMenu, L("Open STL/OBJ/AMF…\tCtrl+O"), L('Open a model'), sub {
|
||||||
$self->{plater}->add if $self->{plater};
|
$self->{plater}->add if $self->{plater};
|
||||||
}, undef, undef); #'brick_add.png');
|
}, undef, undef); #'brick_add.png');
|
||||||
$self->_append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub {
|
$self->_append_menu_item($fileMenu, L("&Load Config…\tCtrl+L"), L('Load exported configuration file'), sub {
|
||||||
$self->load_config_file;
|
$self->load_config_file;
|
||||||
}, undef, 'plugin_add.png');
|
}, undef, 'plugin_add.png');
|
||||||
$self->_append_menu_item($fileMenu, "&Export Config…\tCtrl+E", 'Export current configuration to file', sub {
|
$self->_append_menu_item($fileMenu, L("&Export Config…\tCtrl+E"), L('Export current configuration to file'), sub {
|
||||||
$self->export_config;
|
$self->export_config;
|
||||||
}, undef, 'plugin_go.png');
|
}, undef, 'plugin_go.png');
|
||||||
$self->_append_menu_item($fileMenu, "&Load Config Bundle…", 'Load presets from a bundle', sub {
|
$self->_append_menu_item($fileMenu, L("&Load Config Bundle…"), L('Load presets from a bundle'), sub {
|
||||||
$self->load_configbundle;
|
$self->load_configbundle;
|
||||||
}, undef, 'lorry_add.png');
|
}, undef, 'lorry_add.png');
|
||||||
$self->_append_menu_item($fileMenu, "&Export Config Bundle…", 'Export all presets to file', sub {
|
$self->_append_menu_item($fileMenu, L("&Export Config Bundle…"), L('Export all presets to file'), sub {
|
||||||
$self->export_configbundle;
|
$self->export_configbundle;
|
||||||
}, undef, 'lorry_go.png');
|
}, undef, 'lorry_go.png');
|
||||||
$fileMenu->AppendSeparator();
|
$fileMenu->AppendSeparator();
|
||||||
my $repeat;
|
my $repeat;
|
||||||
$self->_append_menu_item($fileMenu, "Q&uick Slice…\tCtrl+U", 'Slice a file into a G-code', sub {
|
$self->_append_menu_item($fileMenu, L("Q&uick Slice…\tCtrl+U"), L('Slice a file into a G-code'), sub {
|
||||||
wxTheApp->CallAfter(sub {
|
wxTheApp->CallAfter(sub {
|
||||||
$self->quick_slice;
|
$self->quick_slice;
|
||||||
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
|
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
|
||||||
});
|
});
|
||||||
}, undef, 'cog_go.png');
|
}, undef, 'cog_go.png');
|
||||||
$self->_append_menu_item($fileMenu, "Quick Slice and Save &As…\tCtrl+Alt+U", 'Slice a file into a G-code, save as', sub {
|
$self->_append_menu_item($fileMenu, L("Quick Slice and Save &As…\tCtrl+Alt+U"), L('Slice a file into a G-code, save as'), sub {
|
||||||
wxTheApp->CallAfter(sub {
|
wxTheApp->CallAfter(sub {
|
||||||
$self->quick_slice(save_as => 1);
|
$self->quick_slice(save_as => 1);
|
||||||
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
|
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
|
||||||
});
|
});
|
||||||
}, undef, 'cog_go.png');
|
}, undef, 'cog_go.png');
|
||||||
$repeat = $self->_append_menu_item($fileMenu, "&Repeat Last Quick Slice\tCtrl+Shift+U", 'Repeat last quick slice', sub {
|
$repeat = $self->_append_menu_item($fileMenu, L("&Repeat Last Quick Slice\tCtrl+Shift+U"), L('Repeat last quick slice'), sub {
|
||||||
wxTheApp->CallAfter(sub {
|
wxTheApp->CallAfter(sub {
|
||||||
$self->quick_slice(reslice => 1);
|
$self->quick_slice(reslice => 1);
|
||||||
});
|
});
|
||||||
}, undef, 'cog_go.png');
|
}, undef, 'cog_go.png');
|
||||||
$repeat->Enable(0);
|
$repeat->Enable(0);
|
||||||
$fileMenu->AppendSeparator();
|
$fileMenu->AppendSeparator();
|
||||||
$self->_append_menu_item($fileMenu, "Slice to SV&G…\tCtrl+G", 'Slice file to a multi-layer SVG', sub {
|
$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);
|
$self->quick_slice(save_as => 1, export_svg => 1);
|
||||||
}, undef, 'shape_handles.png');
|
}, undef, 'shape_handles.png');
|
||||||
$self->{menu_item_reslice_now} = $self->_append_menu_item(
|
$self->{menu_item_reslice_now} = $self->_append_menu_item(
|
||||||
$fileMenu, "(&Re)Slice Now\tCtrl+S", 'Start new slicing process',
|
$fileMenu, L("(&Re)Slice Now\tCtrl+S"), L('Start new slicing process'),
|
||||||
sub { $self->reslice_now; }, undef, 'shape_handles.png');
|
sub { $self->reslice_now; }, undef, 'shape_handles.png');
|
||||||
$fileMenu->AppendSeparator();
|
$fileMenu->AppendSeparator();
|
||||||
$self->_append_menu_item($fileMenu, "Repair STL file…", 'Automatically repair an STL file', sub {
|
$self->_append_menu_item($fileMenu, L("Repair STL file…"), L('Automatically repair an STL file'), sub {
|
||||||
$self->repair_stl;
|
$self->repair_stl;
|
||||||
}, undef, 'wrench.png');
|
}, undef, 'wrench.png');
|
||||||
$fileMenu->AppendSeparator();
|
$fileMenu->AppendSeparator();
|
||||||
# Cmd+, is standard on OS X - what about other operating systems?
|
# Cmd+, is standard on OS X - what about other operating systems?
|
||||||
$self->_append_menu_item($fileMenu, "Preferences…\tCtrl+,", 'Application preferences', sub {
|
$self->_append_menu_item($fileMenu, L("Preferences…\tCtrl+,"), L('Application preferences'), sub {
|
||||||
Slic3r::GUI::Preferences->new($self)->ShowModal;
|
# Opening the C++ preferences dialog.
|
||||||
|
Slic3r::GUI::open_preferences_dialog($self->{preferences_event});
|
||||||
}, wxID_PREFERENCES);
|
}, wxID_PREFERENCES);
|
||||||
$fileMenu->AppendSeparator();
|
$fileMenu->AppendSeparator();
|
||||||
$self->_append_menu_item($fileMenu, "&Quit", 'Quit Slic3r', sub {
|
$self->_append_menu_item($fileMenu, L("&Quit"), L('Quit Slic3r'), sub {
|
||||||
$self->Close(0);
|
$self->Close(0);
|
||||||
}, wxID_EXIT);
|
}, wxID_EXIT);
|
||||||
}
|
}
|
||||||
|
@ -307,16 +309,16 @@ sub _init_menubar {
|
||||||
my $plater = $self->{plater};
|
my $plater = $self->{plater};
|
||||||
|
|
||||||
$self->{plater_menu} = Wx::Menu->new;
|
$self->{plater_menu} = Wx::Menu->new;
|
||||||
$self->_append_menu_item($self->{plater_menu}, "Export G-code...", 'Export current plate as G-code', sub {
|
$self->_append_menu_item($self->{plater_menu}, L("Export G-code..."), L('Export current plate as G-code'), sub {
|
||||||
$plater->export_gcode;
|
$plater->export_gcode;
|
||||||
}, undef, 'cog_go.png');
|
}, undef, 'cog_go.png');
|
||||||
$self->_append_menu_item($self->{plater_menu}, "Export plate as STL...", 'Export current plate as STL', sub {
|
$self->_append_menu_item($self->{plater_menu}, L("Export plate as STL..."), L('Export current plate as STL'), sub {
|
||||||
$plater->export_stl;
|
$plater->export_stl;
|
||||||
}, undef, 'brick_go.png');
|
}, undef, 'brick_go.png');
|
||||||
$self->_append_menu_item($self->{plater_menu}, "Export plate as AMF...", 'Export current plate as AMF', sub {
|
$self->_append_menu_item($self->{plater_menu}, L("Export plate as AMF..."), L('Export current plate as AMF'), sub {
|
||||||
$plater->export_amf;
|
$plater->export_amf;
|
||||||
}, undef, 'brick_go.png');
|
}, undef, 'brick_go.png');
|
||||||
$self->_append_menu_item($self->{plater_menu}, "Export plate as 3MF...", 'Export current plate as 3MF', sub {
|
$self->_append_menu_item($self->{plater_menu}, L("Export plate as 3MF..."), L('Export current plate as 3MF'), sub {
|
||||||
$plater->export_3mf;
|
$plater->export_3mf;
|
||||||
}, undef, 'brick_go.png');
|
}, undef, 'brick_go.png');
|
||||||
|
|
||||||
|
@ -329,13 +331,13 @@ sub _init_menubar {
|
||||||
{
|
{
|
||||||
my $tab_offset = 0;
|
my $tab_offset = 0;
|
||||||
if (!$self->{no_plater}) {
|
if (!$self->{no_plater}) {
|
||||||
$self->_append_menu_item($windowMenu, "Select &Plater Tab\tCtrl+1", 'Show the plater', sub {
|
$self->_append_menu_item($windowMenu, L("Select &Plater Tab\tCtrl+1"), L('Show the plater'), sub {
|
||||||
$self->select_tab(0);
|
$self->select_tab(0);
|
||||||
}, undef, 'application_view_tile.png');
|
}, undef, 'application_view_tile.png');
|
||||||
$tab_offset += 1;
|
$tab_offset += 1;
|
||||||
}
|
}
|
||||||
if (!$self->{no_controller}) {
|
if (!$self->{no_controller}) {
|
||||||
$self->_append_menu_item($windowMenu, "Select &Controller Tab\tCtrl+T", 'Show the printer controller', sub {
|
$self->_append_menu_item($windowMenu, L("Select &Controller Tab\tCtrl+T"), L('Show the printer controller'), sub {
|
||||||
$self->select_tab(1);
|
$self->select_tab(1);
|
||||||
}, undef, 'printer_empty.png');
|
}, undef, 'printer_empty.png');
|
||||||
$tab_offset += 1;
|
$tab_offset += 1;
|
||||||
|
@ -343,13 +345,13 @@ sub _init_menubar {
|
||||||
if ($tab_offset > 0) {
|
if ($tab_offset > 0) {
|
||||||
$windowMenu->AppendSeparator();
|
$windowMenu->AppendSeparator();
|
||||||
}
|
}
|
||||||
$self->_append_menu_item($windowMenu, "Select P&rint Settings Tab\tCtrl+2", 'Show the print settings', sub {
|
$self->_append_menu_item($windowMenu, L("Select P&rint Settings Tab\tCtrl+2"), L('Show the print settings'), sub {
|
||||||
$self->select_tab($tab_offset+0);
|
$self->select_tab($tab_offset+0);
|
||||||
}, undef, 'cog.png');
|
}, undef, 'cog.png');
|
||||||
$self->_append_menu_item($windowMenu, "Select &Filament Settings Tab\tCtrl+3", 'Show the filament settings', sub {
|
$self->_append_menu_item($windowMenu, L("Select &Filament Settings Tab\tCtrl+3"), L('Show the filament settings'), sub {
|
||||||
$self->select_tab($tab_offset+1);
|
$self->select_tab($tab_offset+1);
|
||||||
}, undef, 'spool.png');
|
}, undef, 'spool.png');
|
||||||
$self->_append_menu_item($windowMenu, "Select Print&er Settings Tab\tCtrl+4", 'Show the printer settings', sub {
|
$self->_append_menu_item($windowMenu, L("Select Print&er Settings Tab\tCtrl+4"), L('Show the printer settings'), sub {
|
||||||
$self->select_tab($tab_offset+2);
|
$self->select_tab($tab_offset+2);
|
||||||
}, undef, 'printer_empty.png');
|
}, undef, 'printer_empty.png');
|
||||||
}
|
}
|
||||||
|
@ -361,66 +363,66 @@ sub _init_menubar {
|
||||||
# as the simple numeric accelerators spoil all numeric data entry.
|
# as the simple numeric accelerators spoil all numeric data entry.
|
||||||
# The camera control accelerators are captured by 3DScene Perl module instead.
|
# The camera control accelerators are captured by 3DScene Perl module instead.
|
||||||
my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
|
my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
|
||||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Iso', '0'), 'Iso View' , sub { $self->select_view('iso' ); });
|
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Iso'), '0'), L('Iso View') , sub { $self->select_view('iso' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Top', '1'), 'Top View' , sub { $self->select_view('top' ); });
|
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Top'), '1'), L('Top View') , sub { $self->select_view('top' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Bottom', '2'), 'Bottom View' , sub { $self->select_view('bottom' ); });
|
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Bottom'), '2'), L('Bottom View') , sub { $self->select_view('bottom' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Front', '3'), 'Front View' , sub { $self->select_view('front' ); });
|
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Front'), '3'), L('Front View') , sub { $self->select_view('front' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Rear', '4'), 'Rear View' , sub { $self->select_view('rear' ); });
|
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Rear'), '4'), L('Rear View') , sub { $self->select_view('rear' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Left', '5'), 'Left View' , sub { $self->select_view('left' ); });
|
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Left'), '5'), L('Left View') , sub { $self->select_view('left' ); });
|
||||||
$self->_append_menu_item($self->{viewMenu}, $accel->('Right', '6'), 'Right View' , sub { $self->select_view('right' ); });
|
$self->_append_menu_item($self->{viewMenu}, $accel->(L('Right'), '6'), L('Right View') , sub { $self->select_view('right' ); });
|
||||||
}
|
}
|
||||||
|
|
||||||
# Help menu
|
# Help menu
|
||||||
my $helpMenu = Wx::Menu->new;
|
my $helpMenu = Wx::Menu->new;
|
||||||
{
|
{
|
||||||
$self->_append_menu_item($helpMenu, "&Configuration $Slic3r::GUI::ConfigWizard::wizard…", "Run Configuration $Slic3r::GUI::ConfigWizard::wizard", sub {
|
$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.
|
# Run the config wizard, offer the "reset user profile" checkbox.
|
||||||
$self->config_wizard(0);
|
$self->config_wizard(0);
|
||||||
});
|
});
|
||||||
$helpMenu->AppendSeparator();
|
$helpMenu->AppendSeparator();
|
||||||
$self->_append_menu_item($helpMenu, "Prusa 3D Drivers", 'Open the Prusa3D drivers download page in your browser', sub {
|
$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/');
|
Wx::LaunchDefaultBrowser('http://www.prusa3d.com/drivers/');
|
||||||
});
|
});
|
||||||
$self->_append_menu_item($helpMenu, "Prusa Edition Releases", 'Open the Prusa Edition releases page in your browser', sub {
|
$self->_append_menu_item($helpMenu, L("Prusa Edition Releases"), L('Open the Prusa Edition releases page in your browser'), sub {
|
||||||
Wx::LaunchDefaultBrowser('http://github.com/prusa3d/slic3r/releases');
|
Wx::LaunchDefaultBrowser('http://github.com/prusa3d/slic3r/releases');
|
||||||
});
|
});
|
||||||
# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", 'Check for new Slic3r versions', sub {
|
# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", 'Check for new Slic3r versions', sub {
|
||||||
# wxTheApp->check_version(1);
|
# wxTheApp->check_version(1);
|
||||||
# });
|
# });
|
||||||
# $versioncheck->Enable(wxTheApp->have_version_check);
|
# $versioncheck->Enable(wxTheApp->have_version_check);
|
||||||
$self->_append_menu_item($helpMenu, "Slic3r &Website", 'Open the Slic3r website in your browser', sub {
|
$self->_append_menu_item($helpMenu, L("Slic3r &Website"), L('Open the Slic3r website in your browser'), sub {
|
||||||
Wx::LaunchDefaultBrowser('http://slic3r.org/');
|
Wx::LaunchDefaultBrowser('http://slic3r.org/');
|
||||||
});
|
});
|
||||||
$self->_append_menu_item($helpMenu, "Slic3r &Manual", 'Open the Slic3r manual in your browser', sub {
|
$self->_append_menu_item($helpMenu, L("Slic3r &Manual"), L('Open the Slic3r manual in your browser'), sub {
|
||||||
Wx::LaunchDefaultBrowser('http://manual.slic3r.org/');
|
Wx::LaunchDefaultBrowser('http://manual.slic3r.org/');
|
||||||
});
|
});
|
||||||
$helpMenu->AppendSeparator();
|
$helpMenu->AppendSeparator();
|
||||||
$self->_append_menu_item($helpMenu, "System Info", 'Show system information', sub {
|
$self->_append_menu_item($helpMenu, L("System Info"), L('Show system information'), sub {
|
||||||
wxTheApp->system_info;
|
wxTheApp->system_info;
|
||||||
});
|
});
|
||||||
$self->_append_menu_item($helpMenu, "Report an Issue", 'Report an issue on the Slic3r Prusa Edition', sub {
|
$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');
|
Wx::LaunchDefaultBrowser('http://github.com/prusa3d/slic3r/issues/new');
|
||||||
});
|
});
|
||||||
$self->_append_menu_item($helpMenu, "&About Slic3r", 'Show about dialog', sub {
|
$self->_append_menu_item($helpMenu, L("&About Slic3r"), L('Show about dialog'), sub {
|
||||||
wxTheApp->about;
|
wxTheApp->about;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
# menubar
|
# menubar
|
||||||
# assign menubar to frame after appending items, otherwise special items
|
# assign menubar to frame after appending items, otherwise special items
|
||||||
# will not be handled correctly
|
# will not be handled correctly
|
||||||
{
|
{
|
||||||
my $menubar = Wx::MenuBar->new;
|
my $menubar = Wx::MenuBar->new;
|
||||||
$menubar->Append($fileMenu, "&File");
|
$menubar->Append($fileMenu, L("&File"));
|
||||||
$menubar->Append($self->{plater_menu}, "&Plater") if $self->{plater_menu};
|
$menubar->Append($self->{plater_menu}, L("&Plater")) if $self->{plater_menu};
|
||||||
$menubar->Append($self->{object_menu}, "&Object") if $self->{object_menu};
|
$menubar->Append($self->{object_menu}, L("&Object")) if $self->{object_menu};
|
||||||
$menubar->Append($windowMenu, "&Window");
|
$menubar->Append($windowMenu, L("&Window"));
|
||||||
$menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu};
|
$menubar->Append($self->{viewMenu}, L("&View")) if $self->{viewMenu};
|
||||||
# Add an optional debug menu
|
# Add an optional debug menu
|
||||||
# (Select application language from the list of installed languages)
|
# (Select application language from the list of installed languages)
|
||||||
# In production code, the add_debug_menu() call should do nothing.
|
|
||||||
Slic3r::GUI::add_debug_menu($menubar, $self->{lang_ch_event});
|
Slic3r::GUI::add_debug_menu($menubar, $self->{lang_ch_event});
|
||||||
$menubar->Append($helpMenu, "&Help");
|
$menubar->Append($helpMenu, L("&Help"));
|
||||||
|
# Add an optional debug menu. In production code, the add_debug_menu() call should do nothing.
|
||||||
$self->SetMenuBar($menubar);
|
$self->SetMenuBar($menubar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -451,7 +453,7 @@ sub quick_slice {
|
||||||
# select input file
|
# select input file
|
||||||
my $input_file;
|
my $input_file;
|
||||||
if (!$params{reslice}) {
|
if (!$params{reslice}) {
|
||||||
my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):',
|
my $dialog = Wx::FileDialog->new($self, L('Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):'),
|
||||||
wxTheApp->{app_config}->get_last_dir, "",
|
wxTheApp->{app_config}->get_last_dir, "",
|
||||||
&Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
&Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
if ($dialog->ShowModal != wxID_OK) {
|
if ($dialog->ShowModal != wxID_OK) {
|
||||||
|
@ -463,13 +465,13 @@ sub quick_slice {
|
||||||
$qs_last_input_file = $input_file unless $params{export_svg};
|
$qs_last_input_file = $input_file unless $params{export_svg};
|
||||||
} else {
|
} else {
|
||||||
if (!defined $qs_last_input_file) {
|
if (!defined $qs_last_input_file) {
|
||||||
Wx::MessageDialog->new($self, "No previously sliced file.",
|
Wx::MessageDialog->new($self, L("No previously sliced file."),
|
||||||
'Error', wxICON_ERROR | wxOK)->ShowModal();
|
L('Error'), wxICON_ERROR | wxOK)->ShowModal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (! -e $qs_last_input_file) {
|
if (! -e $qs_last_input_file) {
|
||||||
Wx::MessageDialog->new($self, "Previously sliced file ($qs_last_input_file) not found.",
|
Wx::MessageDialog->new($self, L("Previously sliced file (").$qs_last_input_file.L(") not found."),
|
||||||
'File Not Found', wxICON_ERROR | wxOK)->ShowModal();
|
L('File Not Found'), wxICON_ERROR | wxOK)->ShowModal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$input_file = $qs_last_input_file;
|
$input_file = $qs_last_input_file;
|
||||||
|
@ -508,7 +510,7 @@ sub quick_slice {
|
||||||
# The following line may die if the output_filename_format template substitution fails.
|
# The following line may die if the output_filename_format template substitution fails.
|
||||||
$output_file = $sprint->output_filepath;
|
$output_file = $sprint->output_filepath;
|
||||||
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg};
|
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg};
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:',
|
my $dlg = Wx::FileDialog->new($self, L('Save ') . ($params{export_svg} ? L('SVG') : L('G-code')) . L(' file as:'),
|
||||||
wxTheApp->{app_config}->get_last_output_dir(dirname($output_file)),
|
wxTheApp->{app_config}->get_last_output_dir(dirname($output_file)),
|
||||||
basename($output_file), $params{export_svg} ? &Slic3r::GUI::FILE_WILDCARDS->{svg} : &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
basename($output_file), $params{export_svg} ? &Slic3r::GUI::FILE_WILDCARDS->{svg} : &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
if ($dlg->ShowModal != wxID_OK) {
|
if ($dlg->ShowModal != wxID_OK) {
|
||||||
|
@ -522,7 +524,7 @@ sub quick_slice {
|
||||||
}
|
}
|
||||||
|
|
||||||
# show processbar dialog
|
# show processbar dialog
|
||||||
$progress_dialog = Wx::ProgressDialog->new('Slicing…', "Processing $input_file_basename…",
|
$progress_dialog = Wx::ProgressDialog->new(L('Slicing…'), L("Processing ").$input_file_basename."…",
|
||||||
100, $self, 0);
|
100, $self, 0);
|
||||||
$progress_dialog->Pulse;
|
$progress_dialog->Pulse;
|
||||||
|
|
||||||
|
@ -542,9 +544,9 @@ sub quick_slice {
|
||||||
$progress_dialog->Destroy;
|
$progress_dialog->Destroy;
|
||||||
undef $progress_dialog;
|
undef $progress_dialog;
|
||||||
|
|
||||||
my $message = "$input_file_basename was successfully sliced.";
|
my $message = $input_file_basename.L(" was successfully sliced.");
|
||||||
wxTheApp->notify($message);
|
wxTheApp->notify($message);
|
||||||
Wx::MessageDialog->new($self, $message, 'Slicing Done!',
|
Wx::MessageDialog->new($self, $message, L('Slicing Done!'),
|
||||||
wxOK | wxICON_INFORMATION)->ShowModal;
|
wxOK | wxICON_INFORMATION)->ShowModal;
|
||||||
};
|
};
|
||||||
Slic3r::GUI::catch_error($self, sub { $progress_dialog->Destroy if $progress_dialog });
|
Slic3r::GUI::catch_error($self, sub { $progress_dialog->Destroy if $progress_dialog });
|
||||||
|
@ -560,7 +562,7 @@ sub repair_stl {
|
||||||
|
|
||||||
my $input_file;
|
my $input_file;
|
||||||
{
|
{
|
||||||
my $dialog = Wx::FileDialog->new($self, 'Select the STL file to repair:',
|
my $dialog = Wx::FileDialog->new($self, L('Select the STL file to repair:'),
|
||||||
wxTheApp->{app_config}->get_last_dir, "",
|
wxTheApp->{app_config}->get_last_dir, "",
|
||||||
&Slic3r::GUI::FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
&Slic3r::GUI::FILE_WILDCARDS->{stl}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
if ($dialog->ShowModal != wxID_OK) {
|
if ($dialog->ShowModal != wxID_OK) {
|
||||||
|
@ -574,7 +576,7 @@ sub repair_stl {
|
||||||
my $output_file = $input_file;
|
my $output_file = $input_file;
|
||||||
{
|
{
|
||||||
$output_file =~ s/\.[sS][tT][lL]$/_fixed.obj/;
|
$output_file =~ s/\.[sS][tT][lL]$/_fixed.obj/;
|
||||||
my $dlg = Wx::FileDialog->new($self, "Save OBJ file (less prone to coordinate errors than STL) as:", dirname($output_file),
|
my $dlg = Wx::FileDialog->new($self, L("Save OBJ file (less prone to coordinate errors than STL) as:"), dirname($output_file),
|
||||||
basename($output_file), &Slic3r::GUI::FILE_WILDCARDS->{obj}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
basename($output_file), &Slic3r::GUI::FILE_WILDCARDS->{obj}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
if ($dlg->ShowModal != wxID_OK) {
|
if ($dlg->ShowModal != wxID_OK) {
|
||||||
$dlg->Destroy;
|
$dlg->Destroy;
|
||||||
|
@ -588,7 +590,7 @@ sub repair_stl {
|
||||||
$tmesh->ReadSTLFile($input_file);
|
$tmesh->ReadSTLFile($input_file);
|
||||||
$tmesh->repair;
|
$tmesh->repair;
|
||||||
$tmesh->WriteOBJFile($output_file);
|
$tmesh->WriteOBJFile($output_file);
|
||||||
Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair");
|
Slic3r::GUI::show_info($self, L("Your file was repaired."), L("Repair"));
|
||||||
}
|
}
|
||||||
|
|
||||||
sub export_config {
|
sub export_config {
|
||||||
|
@ -599,7 +601,7 @@ sub export_config {
|
||||||
eval { $config->validate; };
|
eval { $config->validate; };
|
||||||
Slic3r::GUI::catch_error($self) and return;
|
Slic3r::GUI::catch_error($self) and return;
|
||||||
# Ask user for the file name for the config file.
|
# Ask user for the file name for the config file.
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Save configuration as:',
|
my $dlg = Wx::FileDialog->new($self, L('Save configuration as:'),
|
||||||
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
||||||
$last_config ? basename($last_config) : "config.ini",
|
$last_config ? basename($last_config) : "config.ini",
|
||||||
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
|
@ -617,7 +619,7 @@ sub load_config_file {
|
||||||
my ($self, $file) = @_;
|
my ($self, $file) = @_;
|
||||||
if (!$file) {
|
if (!$file) {
|
||||||
return unless $self->check_unsaved_changes;
|
return unless $self->check_unsaved_changes;
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:',
|
my $dlg = Wx::FileDialog->new($self, L('Select configuration to load:'),
|
||||||
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
||||||
"config.ini",
|
"config.ini",
|
||||||
'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
|
@ -640,7 +642,7 @@ sub export_configbundle {
|
||||||
eval { wxTheApp->{preset_bundle}->full_config->validate; };
|
eval { wxTheApp->{preset_bundle}->full_config->validate; };
|
||||||
Slic3r::GUI::catch_error($self) and return;
|
Slic3r::GUI::catch_error($self) and return;
|
||||||
# Ask user for a file name.
|
# Ask user for a file name.
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:',
|
my $dlg = Wx::FileDialog->new($self, L('Save presets bundle as:'),
|
||||||
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
||||||
"Slic3r_config_bundle.ini",
|
"Slic3r_config_bundle.ini",
|
||||||
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
|
@ -661,7 +663,7 @@ sub load_configbundle {
|
||||||
my ($self, $file, $reset_user_profile) = @_;
|
my ($self, $file, $reset_user_profile) = @_;
|
||||||
return unless $self->check_unsaved_changes;
|
return unless $self->check_unsaved_changes;
|
||||||
if (!$file) {
|
if (!$file) {
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:',
|
my $dlg = Wx::FileDialog->new($self, L('Select configuration to load:'),
|
||||||
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
$last_config ? dirname($last_config) : wxTheApp->{app_config}->get_last_dir,
|
||||||
"config.ini",
|
"config.ini",
|
||||||
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
|
@ -681,7 +683,7 @@ sub load_configbundle {
|
||||||
$tab->load_current_preset;
|
$tab->load_current_preset;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $message = sprintf "%d presets successfully imported.", $presets_imported;
|
my $message = sprintf L("%d presets successfully imported."), $presets_imported;
|
||||||
Slic3r::GUI::show_info($self, $message);
|
Slic3r::GUI::show_info($self, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -743,8 +745,8 @@ sub check_unsaved_changes {
|
||||||
|
|
||||||
if (@dirty) {
|
if (@dirty) {
|
||||||
my $titles = join ', ', @dirty;
|
my $titles = join ', ', @dirty;
|
||||||
my $confirm = Wx::MessageDialog->new($self, "You have unsaved changes ($titles). Discard changes and continue anyway?",
|
my $confirm = Wx::MessageDialog->new($self, L("You have unsaved changes ").($titles).L(". Discard changes and continue anyway?"),
|
||||||
'Unsaved Presets', wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
|
L('Unsaved Presets'), wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
|
||||||
return $confirm->ShowModal == wxID_YES;
|
return $confirm->ShowModal == wxID_YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@ use constant TB_CUT => &Wx::NewId;
|
||||||
use constant TB_SETTINGS => &Wx::NewId;
|
use constant TB_SETTINGS => &Wx::NewId;
|
||||||
use constant TB_LAYER_EDITING => &Wx::NewId;
|
use constant TB_LAYER_EDITING => &Wx::NewId;
|
||||||
|
|
||||||
|
use Wx::Locale gettext => 'L';
|
||||||
|
|
||||||
# package variables to avoid passing lexicals to threads
|
# package variables to avoid passing lexicals to threads
|
||||||
our $PROGRESS_BAR_EVENT : shared = Wx::NewEventType;
|
our $PROGRESS_BAR_EVENT : shared = Wx::NewEventType;
|
||||||
our $ERROR_EVENT : shared = Wx::NewEventType;
|
our $ERROR_EVENT : shared = Wx::NewEventType;
|
||||||
|
@ -50,7 +52,7 @@ sub new {
|
||||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||||
$self->{config} = Slic3r::Config::new_from_defaults_keys([qw(
|
$self->{config} = Slic3r::Config::new_from_defaults_keys([qw(
|
||||||
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
|
bed_shape complete_objects extruder_clearance_radius skirts skirt_distance brim_width variable_layer_height
|
||||||
serial_port serial_speed octoprint_host octoprint_apikey
|
serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile
|
||||||
nozzle_diameter single_extruder_multi_material
|
nozzle_diameter single_extruder_multi_material
|
||||||
wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe wipe_tower_rotation_angle extruder_colour filament_colour
|
wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe wipe_tower_rotation_angle extruder_colour filament_colour
|
||||||
)]);
|
)]);
|
||||||
|
@ -99,7 +101,7 @@ sub new {
|
||||||
# Initialize 3D plater
|
# Initialize 3D plater
|
||||||
if ($Slic3r::GUI::have_OpenGL) {
|
if ($Slic3r::GUI::have_OpenGL) {
|
||||||
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config});
|
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config});
|
||||||
$self->{preview_notebook}->AddPage($self->{canvas3D}, '3D');
|
$self->{preview_notebook}->AddPage($self->{canvas3D}, L('3D'));
|
||||||
$self->{canvas3D}->set_on_select_object($on_select_object);
|
$self->{canvas3D}->set_on_select_object($on_select_object);
|
||||||
$self->{canvas3D}->set_on_double_click($on_double_click);
|
$self->{canvas3D}->set_on_double_click($on_double_click);
|
||||||
$self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); });
|
$self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); });
|
||||||
|
@ -133,7 +135,7 @@ sub new {
|
||||||
|
|
||||||
# Initialize 2D preview canvas
|
# Initialize 2D preview canvas
|
||||||
$self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config});
|
$self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config});
|
||||||
$self->{preview_notebook}->AddPage($self->{canvas}, '2D');
|
$self->{preview_notebook}->AddPage($self->{canvas}, L('2D'));
|
||||||
$self->{canvas}->on_select_object($on_select_object);
|
$self->{canvas}->on_select_object($on_select_object);
|
||||||
$self->{canvas}->on_double_click($on_double_click);
|
$self->{canvas}->on_double_click($on_double_click);
|
||||||
$self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); });
|
$self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); });
|
||||||
|
@ -145,14 +147,14 @@ sub new {
|
||||||
$self->{preview3D}->canvas->on_viewport_changed(sub {
|
$self->{preview3D}->canvas->on_viewport_changed(sub {
|
||||||
$self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas);
|
$self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas);
|
||||||
});
|
});
|
||||||
$self->{preview_notebook}->AddPage($self->{preview3D}, 'Preview');
|
$self->{preview_notebook}->AddPage($self->{preview3D}, L('Preview'));
|
||||||
$self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1;
|
$self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize toolpaths preview
|
# Initialize toolpaths preview
|
||||||
if ($Slic3r::GUI::have_OpenGL) {
|
if ($Slic3r::GUI::have_OpenGL) {
|
||||||
$self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print});
|
$self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print});
|
||||||
$self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Layers');
|
$self->{preview_notebook}->AddPage($self->{toolpaths2D}, L('Layers'));
|
||||||
}
|
}
|
||||||
|
|
||||||
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub {
|
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub {
|
||||||
|
@ -172,37 +174,37 @@ sub new {
|
||||||
if (!&Wx::wxMSW) {
|
if (!&Wx::wxMSW) {
|
||||||
Wx::ToolTip::Enable(1);
|
Wx::ToolTip::Enable(1);
|
||||||
$self->{htoolbar} = Wx::ToolBar->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL);
|
$self->{htoolbar} = Wx::ToolBar->new($self, -1, wxDefaultPosition, wxDefaultSize, wxTB_HORIZONTAL | wxTB_TEXT | wxBORDER_SIMPLE | wxTAB_TRAVERSAL);
|
||||||
$self->{htoolbar}->AddTool(TB_ADD, "Add…", Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_ADD, L("Add…"), Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_REMOVE, "Delete", Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_REMOVE, L("Delete"), Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_RESET, "Delete All", Wx::Bitmap->new(Slic3r::var("cross.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_RESET, L("Delete All"), Wx::Bitmap->new(Slic3r::var("cross.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_ARRANGE, "Arrange", Wx::Bitmap->new(Slic3r::var("bricks.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_ARRANGE, L("Arrange"), Wx::Bitmap->new(Slic3r::var("bricks.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddSeparator;
|
$self->{htoolbar}->AddSeparator;
|
||||||
$self->{htoolbar}->AddTool(TB_MORE, "More", Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_MORE, L("More"), Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_FEWER, "Fewer", Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_FEWER, L("Fewer"), Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddSeparator;
|
$self->{htoolbar}->AddSeparator;
|
||||||
$self->{htoolbar}->AddTool(TB_45CCW, "45° ccw", Wx::Bitmap->new(Slic3r::var("arrow_rotate_anticlockwise.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_45CCW, L("45° ccw"), Wx::Bitmap->new(Slic3r::var("arrow_rotate_anticlockwise.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_45CW, "45° cw", Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_45CW, L("45° cw"), Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_SCALE, "Scale…", Wx::Bitmap->new(Slic3r::var("arrow_out.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_SCALE, L("Scale…"), Wx::Bitmap->new(Slic3r::var("arrow_out.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_SPLIT, L("Split"), Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_CUT, L("Cut…"), Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddSeparator;
|
$self->{htoolbar}->AddSeparator;
|
||||||
$self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG), '');
|
$self->{htoolbar}->AddTool(TB_SETTINGS, L("Settings…"), Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG), '');
|
||||||
$self->{htoolbar}->AddTool(TB_LAYER_EDITING, 'Layer Editing', Wx::Bitmap->new(Slic3r::var("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing');
|
$self->{htoolbar}->AddTool(TB_LAYER_EDITING, L('Layer Editing'), Wx::Bitmap->new(Slic3r::var("variable_layer_height.png"), wxBITMAP_TYPE_PNG), wxNullBitmap, 1, 0, 'Layer Editing');
|
||||||
} else {
|
} else {
|
||||||
my %tbar_buttons = (
|
my %tbar_buttons = (
|
||||||
add => "Add…",
|
add => L("Add…"),
|
||||||
remove => "Delete",
|
remove => L("Delete"),
|
||||||
reset => "Delete All",
|
reset => L("Delete All"),
|
||||||
arrange => "Arrange",
|
arrange => L("Arrange"),
|
||||||
increase => "",
|
increase => "",
|
||||||
decrease => "",
|
decrease => "",
|
||||||
rotate45ccw => "",
|
rotate45ccw => "",
|
||||||
rotate45cw => "",
|
rotate45cw => "",
|
||||||
changescale => "Scale…",
|
changescale => L("Scale…"),
|
||||||
split => "Split",
|
split => L("Split"),
|
||||||
cut => "Cut…",
|
cut => L("Cut…"),
|
||||||
settings => "Settings…",
|
settings => L("Settings…"),
|
||||||
layer_editing => "Layer editing",
|
layer_editing => L("Layer editing"),
|
||||||
);
|
);
|
||||||
$self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
|
$self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) {
|
for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) {
|
||||||
|
@ -215,9 +217,9 @@ sub new {
|
||||||
|
|
||||||
$self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize,
|
$self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize,
|
||||||
wxLC_SINGLE_SEL | wxLC_REPORT | wxBORDER_SUNKEN | wxTAB_TRAVERSAL | wxWANTS_CHARS );
|
wxLC_SINGLE_SEL | wxLC_REPORT | wxBORDER_SUNKEN | wxTAB_TRAVERSAL | wxWANTS_CHARS );
|
||||||
$self->{list}->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 145);
|
$self->{list}->InsertColumn(0, L("Name"), wxLIST_FORMAT_LEFT, 145);
|
||||||
$self->{list}->InsertColumn(1, "Copies", wxLIST_FORMAT_CENTER, 45);
|
$self->{list}->InsertColumn(1, L("Copies"), wxLIST_FORMAT_CENTER, 45);
|
||||||
$self->{list}->InsertColumn(2, "Scale", wxLIST_FORMAT_CENTER, wxLIST_AUTOSIZE_USEHEADER);
|
$self->{list}->InsertColumn(2, L("Scale"), wxLIST_FORMAT_CENTER, wxLIST_AUTOSIZE_USEHEADER);
|
||||||
EVT_LIST_ITEM_SELECTED($self, $self->{list}, \&list_item_selected);
|
EVT_LIST_ITEM_SELECTED($self, $self->{list}, \&list_item_selected);
|
||||||
EVT_LIST_ITEM_DESELECTED($self, $self->{list}, \&list_item_deselected);
|
EVT_LIST_ITEM_DESELECTED($self, $self->{list}, \&list_item_deselected);
|
||||||
EVT_LIST_ITEM_ACTIVATED($self, $self->{list}, \&list_item_activated);
|
EVT_LIST_ITEM_ACTIVATED($self, $self->{list}, \&list_item_activated);
|
||||||
|
@ -231,11 +233,11 @@ sub new {
|
||||||
});
|
});
|
||||||
|
|
||||||
# right pane buttons
|
# right pane buttons
|
||||||
$self->{btn_export_gcode} = Wx::Button->new($self, -1, "Export G-code…", wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
$self->{btn_export_gcode} = Wx::Button->new($self, -1, L("Export G-code…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||||
$self->{btn_reslice} = Wx::Button->new($self, -1, "Slice now", wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
$self->{btn_reslice} = Wx::Button->new($self, -1, L("Slice now"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||||
$self->{btn_print} = Wx::Button->new($self, -1, "Print…", wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
$self->{btn_print} = Wx::Button->new($self, -1, L("Print…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||||
$self->{btn_send_gcode} = Wx::Button->new($self, -1, "Send to printer", wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
$self->{btn_send_gcode} = Wx::Button->new($self, -1, L("Send to printer"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||||
$self->{btn_export_stl} = Wx::Button->new($self, -1, "Export STL…", wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
$self->{btn_export_stl} = Wx::Button->new($self, -1, L("Export STL…"), wxDefaultPosition, [-1, 30], wxBU_LEFT);
|
||||||
#$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font);
|
#$self->{btn_export_gcode}->SetFont($Slic3r::GUI::small_font);
|
||||||
#$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font);
|
#$self->{btn_export_stl}->SetFont($Slic3r::GUI::small_font);
|
||||||
$self->{btn_print}->Hide;
|
$self->{btn_print}->Hide;
|
||||||
|
@ -362,9 +364,9 @@ sub new {
|
||||||
$presets->AddGrowableCol(1, 1);
|
$presets->AddGrowableCol(1, 1);
|
||||||
$presets->SetFlexibleDirection(wxHORIZONTAL);
|
$presets->SetFlexibleDirection(wxHORIZONTAL);
|
||||||
my %group_labels = (
|
my %group_labels = (
|
||||||
print => 'Print settings',
|
print => L('Print settings'),
|
||||||
filament => 'Filament',
|
filament => L('Filament'),
|
||||||
printer => 'Printer',
|
printer => L('Printer'),
|
||||||
);
|
);
|
||||||
# UI Combo boxes for a print, multiple filaments, and a printer.
|
# UI Combo boxes for a print, multiple filaments, and a printer.
|
||||||
# Initially a single filament combo box is created, but the number of combo boxes for the filament selection may increase,
|
# Initially a single filament combo box is created, but the number of combo boxes for the filament selection may increase,
|
||||||
|
@ -393,7 +395,7 @@ sub new {
|
||||||
|
|
||||||
my $object_info_sizer;
|
my $object_info_sizer;
|
||||||
{
|
{
|
||||||
my $box = Wx::StaticBox->new($self, -1, "Info");
|
my $box = Wx::StaticBox->new($self, -1, L("Info"));
|
||||||
$object_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
|
$object_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
|
||||||
$object_info_sizer->SetMinSize([350,-1]);
|
$object_info_sizer->SetMinSize([350,-1]);
|
||||||
my $grid_sizer = Wx::FlexGridSizer->new(3, 4, 5, 5);
|
my $grid_sizer = Wx::FlexGridSizer->new(3, 4, 5, 5);
|
||||||
|
@ -403,11 +405,11 @@ sub new {
|
||||||
$object_info_sizer->Add($grid_sizer, 0, wxEXPAND);
|
$object_info_sizer->Add($grid_sizer, 0, wxEXPAND);
|
||||||
|
|
||||||
my @info = (
|
my @info = (
|
||||||
size => "Size",
|
size => L("Size"),
|
||||||
volume => "Volume",
|
volume => L("Volume"),
|
||||||
facets => "Facets",
|
facets => L("Facets"),
|
||||||
materials => "Materials",
|
materials => L("Materials"),
|
||||||
manifold => "Manifold",
|
manifold => L("Manifold"),
|
||||||
);
|
);
|
||||||
while (my $field = shift @info) {
|
while (my $field = shift @info) {
|
||||||
my $label = shift @info;
|
my $label = shift @info;
|
||||||
|
@ -433,7 +435,7 @@ sub new {
|
||||||
|
|
||||||
my $print_info_sizer;
|
my $print_info_sizer;
|
||||||
{
|
{
|
||||||
my $box = Wx::StaticBox->new($self, -1, "Sliced Info");
|
my $box = Wx::StaticBox->new($self, -1, L("Sliced Info"));
|
||||||
$print_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
|
$print_info_sizer = Wx::StaticBoxSizer->new($box, wxVERTICAL);
|
||||||
$print_info_sizer->SetMinSize([350,-1]);
|
$print_info_sizer->SetMinSize([350,-1]);
|
||||||
my $grid_sizer = Wx::FlexGridSizer->new(2, 2, 5, 5);
|
my $grid_sizer = Wx::FlexGridSizer->new(2, 2, 5, 5);
|
||||||
|
@ -442,11 +444,11 @@ sub new {
|
||||||
$grid_sizer->AddGrowableCol(3, 1);
|
$grid_sizer->AddGrowableCol(3, 1);
|
||||||
$print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
|
$print_info_sizer->Add($grid_sizer, 0, wxEXPAND);
|
||||||
my @info = (
|
my @info = (
|
||||||
fil_m => "Used Filament (m)",
|
fil_m => L("Used Filament (m)"),
|
||||||
fil_mm3 => "Used Filament (mm\x{00B3})",
|
fil_mm3 => L("Used Filament (mm³)"),
|
||||||
fil_g => "Used Filament (g)",
|
fil_g => L("Used Filament (g)"),
|
||||||
cost => "Cost",
|
cost => L("Cost"),
|
||||||
time => "Estimated printing time",
|
time => L("Estimated printing time"),
|
||||||
);
|
);
|
||||||
while (my $field = shift @info) {
|
while (my $field = shift @info) {
|
||||||
my $label = shift @info;
|
my $label = shift @info;
|
||||||
|
@ -624,7 +626,7 @@ sub load_files {
|
||||||
my $one_by_one = (@$nozzle_dmrs <= 1) || (@$input_files == 1) ||
|
my $one_by_one = (@$nozzle_dmrs <= 1) || (@$input_files == 1) ||
|
||||||
defined(first { $_ =~ /.[aA][mM][fF]$/ || $_ =~ /.[aA][mM][fF].[xX][mM][lL]$/ || $_ =~ /.[zZ][iI][pP].[aA][mM][fF]$/ || $_ =~ /.3[mM][fF]$/ || $_ =~ /.[pP][rR][uI][sS][aA]$/ } @$input_files);
|
defined(first { $_ =~ /.[aA][mM][fF]$/ || $_ =~ /.[aA][mM][fF].[xX][mM][lL]$/ || $_ =~ /.[zZ][iI][pP].[aA][mM][fF]$/ || $_ =~ /.3[mM][fF]$/ || $_ =~ /.[pP][rR][uI][sS][aA]$/ } @$input_files);
|
||||||
|
|
||||||
my $process_dialog = Wx::ProgressDialog->new('Loading…', "Processing input file\n" . basename($input_files->[0]), 100, $self, 0);
|
my $process_dialog = Wx::ProgressDialog->new(L('Loading…'), L("Processing input file\n") . basename($input_files->[0]), 100, $self, 0);
|
||||||
$process_dialog->Pulse;
|
$process_dialog->Pulse;
|
||||||
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
|
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
|
||||||
|
|
||||||
|
@ -638,7 +640,7 @@ sub load_files {
|
||||||
# For all input files.
|
# For all input files.
|
||||||
for (my $i = 0; $i < @$input_files; $i += 1) {
|
for (my $i = 0; $i < @$input_files; $i += 1) {
|
||||||
my $input_file = $input_files->[$i];
|
my $input_file = $input_files->[$i];
|
||||||
$process_dialog->Update(100. * $i / @$input_files, "Processing input file\n" . basename($input_file));
|
$process_dialog->Update(100. * $i / @$input_files, L("Processing input file\n") . basename($input_file));
|
||||||
|
|
||||||
my $model;
|
my $model;
|
||||||
if (($input_file =~ /.3[mM][fF]$/) || ($input_file =~ /.[zZ][iI][pP].[aA][mM][fF]$/))
|
if (($input_file =~ /.3[mM][fF]$/) || ($input_file =~ /.[zZ][iI][pP].[aA][mM][fF]$/))
|
||||||
|
@ -658,10 +660,10 @@ sub load_files {
|
||||||
|
|
||||||
if ($model->looks_like_multipart_object) {
|
if ($model->looks_like_multipart_object) {
|
||||||
my $dialog = Wx::MessageDialog->new($self,
|
my $dialog = Wx::MessageDialog->new($self,
|
||||||
"This file contains several objects positioned at multiple heights. "
|
L("This file contains several objects positioned at multiple heights. "
|
||||||
. "Instead of considering them as multiple objects, should I consider\n"
|
. "Instead of considering them as multiple objects, should I consider\n"
|
||||||
. "this file as a single object having multiple parts?\n",
|
. "this file as a single object having multiple parts?\n"),
|
||||||
'Multi-part object detected', wxICON_WARNING | wxYES | wxNO);
|
L('Multi-part object detected'), wxICON_WARNING | wxYES | wxNO);
|
||||||
$model->convert_multipart_object if $dialog->ShowModal() == wxID_YES;
|
$model->convert_multipart_object if $dialog->ShowModal() == wxID_YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,10 +677,10 @@ sub load_files {
|
||||||
|
|
||||||
if ($new_model) {
|
if ($new_model) {
|
||||||
my $dialog = Wx::MessageDialog->new($self,
|
my $dialog = Wx::MessageDialog->new($self,
|
||||||
"Multiple objects were loaded for a multi-material printer.\n"
|
L("Multiple objects were loaded for a multi-material printer.\n"
|
||||||
. "Instead of considering them as multiple objects, should I consider\n"
|
. "Instead of considering them as multiple objects, should I consider\n"
|
||||||
. "these files to represent a single object having multiple parts?\n",
|
. "these files to represent a single object having multiple parts?\n"),
|
||||||
'Multi-part object detected', wxICON_WARNING | wxYES | wxNO);
|
L('Multi-part object detected'), wxICON_WARNING | wxYES | wxNO);
|
||||||
$new_model->convert_multipart_object if $dialog->ShowModal() == wxID_YES;
|
$new_model->convert_multipart_object if $dialog->ShowModal() == wxID_YES;
|
||||||
push @obj_idx, $self->load_model_objects(@{$new_model->objects});
|
push @obj_idx, $self->load_model_objects(@{$new_model->objects});
|
||||||
}
|
}
|
||||||
|
@ -687,7 +689,7 @@ sub load_files {
|
||||||
wxTheApp->{app_config}->update_skein_dir(dirname($input_files->[-1]));
|
wxTheApp->{app_config}->update_skein_dir(dirname($input_files->[-1]));
|
||||||
|
|
||||||
$process_dialog->Destroy;
|
$process_dialog->Destroy;
|
||||||
$self->statusbar->SetStatusText("Loaded " . join(',', @loaded_files));
|
$self->statusbar->SetStatusText(L("Loaded ") . join(',', @loaded_files));
|
||||||
return @obj_idx;
|
return @obj_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,8 +741,8 @@ sub load_model_objects {
|
||||||
if ($scaled_down) {
|
if ($scaled_down) {
|
||||||
Slic3r::GUI::show_info(
|
Slic3r::GUI::show_info(
|
||||||
$self,
|
$self,
|
||||||
'Your object appears to be too large, so it was automatically scaled down to fit your print bed.',
|
L('Your object appears to be too large, so it was automatically scaled down to fit your print bed.'),
|
||||||
'Object too large?',
|
L('Object too large?'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -895,7 +897,7 @@ sub set_number_of_copies {
|
||||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||||
|
|
||||||
# prompt user
|
# prompt user
|
||||||
my $copies = Wx::GetNumberFromUser("", "Enter the number of copies of the selected object:", "Copies", $model_object->instances_count, 0, 1000, $self);
|
my $copies = Wx::GetNumberFromUser("", L("Enter the number of copies of the selected object:"), L("Copies"), $model_object->instances_count, 0, 1000, $self);
|
||||||
my $diff = $copies - $model_object->instances_count;
|
my $diff = $copies - $model_object->instances_count;
|
||||||
if ($diff == 0) {
|
if ($diff == 0) {
|
||||||
# no variation
|
# no variation
|
||||||
|
@ -922,9 +924,9 @@ sub _get_number_from_user {
|
||||||
Wx::MessageBox(
|
Wx::MessageBox(
|
||||||
$error_message .
|
$error_message .
|
||||||
(($only_positive && $value <= 0) ?
|
(($only_positive && $value <= 0) ?
|
||||||
": $value\nNon-positive value." :
|
": ".$value.L("\nNon-positive value.") :
|
||||||
": $value\nNot a numeric value."),
|
": ".$value.L("\nNot a numeric value.")),
|
||||||
"Slic3r Error", wxOK | wxICON_EXCLAMATION, $self);
|
L("Slic3r Error"), wxOK | wxICON_EXCLAMATION, $self);
|
||||||
$default = $value;
|
$default = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -945,7 +947,7 @@ sub rotate {
|
||||||
if (!defined $angle) {
|
if (!defined $angle) {
|
||||||
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
|
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
|
||||||
my $default = $axis == Z ? rad2deg($model_instance->rotation) : 0;
|
my $default = $axis == Z ? rad2deg($model_instance->rotation) : 0;
|
||||||
$angle = $self->_get_number_from_user("Enter the rotation angle:", "Rotate around $axis_name axis", "Invalid rotation angle entered", $default);
|
$angle = $self->_get_number_from_user(L("Enter the rotation angle:"), L("Rotate around ").$axis_name.(" axis"), L("Invalid rotation angle entered"), $default);
|
||||||
return if $angle eq '';
|
return if $angle eq '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1025,12 +1027,12 @@ sub changescale {
|
||||||
if ($tosize) {
|
if ($tosize) {
|
||||||
my $cursize = $object_size->[$axis];
|
my $cursize = $object_size->[$axis];
|
||||||
my $newsize = $self->_get_number_from_user(
|
my $newsize = $self->_get_number_from_user(
|
||||||
sprintf('Enter the new size for the selected object (print bed: %smm):', unscale($bed_size->[$axis])),
|
sprintf(L('Enter the new size for the selected object (print bed: %smm):'), unscale($bed_size->[$axis])),
|
||||||
"Scale along $axis_name", 'Invalid scaling value entered', $cursize, 1);
|
L("Scale along ").$axis_name, L('Invalid scaling value entered'), $cursize, 1);
|
||||||
return if $newsize eq '';
|
return if $newsize eq '';
|
||||||
$scale = $newsize / $cursize * 100;
|
$scale = $newsize / $cursize * 100;
|
||||||
} else {
|
} else {
|
||||||
$scale = $self->_get_number_from_user('Enter the scale % for the selected object:', "Scale along $axis_name", 'Invalid scaling value entered', 100, 1);
|
$scale = $self->_get_number_from_user(L('Enter the scale % for the selected object:'), L("Scale along ").$axis_name, L('Invalid scaling value entered'), 100, 1);
|
||||||
return if $scale eq '';
|
return if $scale eq '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1051,12 +1053,12 @@ sub changescale {
|
||||||
my $scale;
|
my $scale;
|
||||||
if ($tosize) {
|
if ($tosize) {
|
||||||
my $cursize = max(@$object_size);
|
my $cursize = max(@$object_size);
|
||||||
my $newsize = $self->_get_number_from_user('Enter the new max size for the selected object:', 'Scale', 'Invalid scaling value entered', $cursize, 1);
|
my $newsize = $self->_get_number_from_user(L('Enter the new max size for the selected object:'), L('Scale'), L('Invalid scaling value entered'), $cursize, 1);
|
||||||
return if ! defined($newsize) || $newsize eq '';
|
return if ! defined($newsize) || $newsize eq '';
|
||||||
$scale = $model_instance->scaling_factor * $newsize / $cursize * 100;
|
$scale = $model_instance->scaling_factor * $newsize / $cursize * 100;
|
||||||
} else {
|
} else {
|
||||||
# max scale factor should be above 2540 to allow importing files exported in inches
|
# max scale factor should be above 2540 to allow importing files exported in inches
|
||||||
$scale = $self->_get_number_from_user('Enter the scale % for the selected object:', 'Scale', 'Invalid scaling value entered', $model_instance->scaling_factor*100, 1);
|
$scale = $self->_get_number_from_user(L('Enter the scale % for the selected object:'), L('Scale'), L('Invalid scaling value entered'), $model_instance->scaling_factor*100, 1);
|
||||||
return if ! defined($scale) || $scale eq '';
|
return if ! defined($scale) || $scale eq '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1107,7 +1109,7 @@ sub split_object {
|
||||||
my $current_model_object = $new_model->get_object($obj_idx);
|
my $current_model_object = $new_model->get_object($obj_idx);
|
||||||
|
|
||||||
if ($current_model_object->volumes_count > 1) {
|
if ($current_model_object->volumes_count > 1) {
|
||||||
Slic3r::GUI::warning_catcher($self)->("The selected object can't be split because it contains more than one volume/material.");
|
Slic3r::GUI::warning_catcher($self)->(L("The selected object can't be split because it contains more than one volume/material."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1116,7 +1118,7 @@ sub split_object {
|
||||||
my @model_objects = @{$current_model_object->split_object};
|
my @model_objects = @{$current_model_object->split_object};
|
||||||
if (@model_objects == 1) {
|
if (@model_objects == 1) {
|
||||||
$self->resume_background_process;
|
$self->resume_background_process;
|
||||||
Slic3r::GUI::warning_catcher($self)->("The selected object couldn't be split because it contains only one part.");
|
Slic3r::GUI::warning_catcher($self)->(L("The selected object couldn't be split because it contains only one part."));
|
||||||
$self->resume_background_process;
|
$self->resume_background_process;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1281,7 +1283,7 @@ sub reslice {
|
||||||
$self->async_apply_config;
|
$self->async_apply_config;
|
||||||
$self->statusbar->SetCancelCallback(sub {
|
$self->statusbar->SetCancelCallback(sub {
|
||||||
$self->stop_background_process;
|
$self->stop_background_process;
|
||||||
$self->statusbar->SetStatusText("Slicing cancelled");
|
$self->statusbar->SetStatusText(L("Slicing cancelled"));
|
||||||
# this updates buttons status
|
# this updates buttons status
|
||||||
$self->object_list_changed;
|
$self->object_list_changed;
|
||||||
});
|
});
|
||||||
|
@ -1295,7 +1297,7 @@ sub export_gcode {
|
||||||
return if !@{$self->{objects}};
|
return if !@{$self->{objects}};
|
||||||
|
|
||||||
if ($self->{export_gcode_output_file}) {
|
if ($self->{export_gcode_output_file}) {
|
||||||
Wx::MessageDialog->new($self, "Another export job is currently running.", 'Error', wxOK | wxICON_ERROR)->ShowModal;
|
Wx::MessageDialog->new($self, L("Another export job is currently running."), L('Error'), wxOK | wxICON_ERROR)->ShowModal;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1326,7 +1328,7 @@ sub export_gcode {
|
||||||
} else {
|
} else {
|
||||||
my $default_output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
|
my $default_output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
|
||||||
Slic3r::GUI::catch_error($self) and return;
|
Slic3r::GUI::catch_error($self) and return;
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:',
|
my $dlg = Wx::FileDialog->new($self, L('Save G-code file as:'),
|
||||||
wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)),
|
wxTheApp->{app_config}->get_last_output_dir(dirname($default_output_file)),
|
||||||
basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
if ($dlg->ShowModal != wxID_OK) {
|
if ($dlg->ShowModal != wxID_OK) {
|
||||||
|
@ -1343,7 +1345,7 @@ sub export_gcode {
|
||||||
|
|
||||||
$self->statusbar->SetCancelCallback(sub {
|
$self->statusbar->SetCancelCallback(sub {
|
||||||
$self->stop_background_process;
|
$self->stop_background_process;
|
||||||
$self->statusbar->SetStatusText("Export cancelled");
|
$self->statusbar->SetStatusText(L("Export cancelled"));
|
||||||
$self->{export_gcode_output_file} = undef;
|
$self->{export_gcode_output_file} = undef;
|
||||||
$self->{send_gcode_file} = undef;
|
$self->{send_gcode_file} = undef;
|
||||||
|
|
||||||
|
@ -1440,24 +1442,29 @@ sub on_export_completed {
|
||||||
if ($result) {
|
if ($result) {
|
||||||
# G-code file exported successfully.
|
# G-code file exported successfully.
|
||||||
if ($self->{print_file}) {
|
if ($self->{print_file}) {
|
||||||
$message = "File added to print queue";
|
$message = L("File added to print queue");
|
||||||
$do_print = 1;
|
$do_print = 1;
|
||||||
} elsif ($self->{send_gcode_file}) {
|
} elsif ($self->{send_gcode_file}) {
|
||||||
$message = "Sending G-code file to the OctoPrint server...";
|
$message = L("Sending G-code file to the OctoPrint server...");
|
||||||
$send_gcode = 1;
|
$send_gcode = 1;
|
||||||
} else {
|
} else {
|
||||||
$message = "G-code file exported to " . $self->{export_gcode_output_file};
|
$message = L("G-code file exported to ") . $self->{export_gcode_output_file};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$message = "Export failed";
|
$message = L("Export failed");
|
||||||
}
|
}
|
||||||
$self->{export_gcode_output_file} = undef;
|
$self->{export_gcode_output_file} = undef;
|
||||||
$self->statusbar->SetStatusText($message);
|
$self->statusbar->SetStatusText($message);
|
||||||
wxTheApp->notify($message);
|
wxTheApp->notify($message);
|
||||||
|
|
||||||
$self->do_print if $do_print;
|
$self->do_print if $do_print;
|
||||||
|
|
||||||
# Send $self->{send_gcode_file} to OctoPrint.
|
# Send $self->{send_gcode_file} to OctoPrint.
|
||||||
$self->send_gcode if $send_gcode;
|
if ($send_gcode) {
|
||||||
|
my $op = Slic3r::OctoPrint->new($self->{config});
|
||||||
|
$op->send_gcode($self->GetId(), $PROGRESS_BAR_EVENT, $ERROR_EVENT, $self->{send_gcode_file});
|
||||||
|
}
|
||||||
|
|
||||||
$self->{print_file} = undef;
|
$self->{print_file} = undef;
|
||||||
$self->{send_gcode_file} = undef;
|
$self->{send_gcode_file} = undef;
|
||||||
$self->{"print_info_cost"}->SetLabel(sprintf("%.2f" , $self->{print}->total_cost));
|
$self->{"print_info_cost"}->SetLabel(sprintf("%.2f" , $self->{print}->total_cost));
|
||||||
|
@ -1486,45 +1493,6 @@ sub do_print {
|
||||||
my $filament_names = wxTheApp->{preset_bundle}->filament_presets;
|
my $filament_names = wxTheApp->{preset_bundle}->filament_presets;
|
||||||
$filament_stats = { map { $filament_names->[$_] => $filament_stats->{$_} } keys %$filament_stats };
|
$filament_stats = { map { $filament_names->[$_] => $filament_stats->{$_} } keys %$filament_stats };
|
||||||
$printer_panel->load_print_job($self->{print_file}, $filament_stats);
|
$printer_panel->load_print_job($self->{print_file}, $filament_stats);
|
||||||
|
|
||||||
$self->GetFrame->select_tab(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Send $self->{send_gcode_file} to OctoPrint.
|
|
||||||
#FIXME Currently this call blocks the UI. Make it asynchronous.
|
|
||||||
sub send_gcode {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
$self->statusbar->StartBusy;
|
|
||||||
|
|
||||||
my $ua = LWP::UserAgent->new;
|
|
||||||
$ua->timeout(180);
|
|
||||||
|
|
||||||
my $res = $ua->post(
|
|
||||||
"http://" . $self->{config}->octoprint_host . "/api/files/local",
|
|
||||||
Content_Type => 'form-data',
|
|
||||||
'X-Api-Key' => $self->{config}->octoprint_apikey,
|
|
||||||
Content => [
|
|
||||||
file => [
|
|
||||||
# On Windows, the path has to be encoded in local code page for perl to be able to open it.
|
|
||||||
Slic3r::encode_path($self->{send_gcode_file}),
|
|
||||||
# Remove the UTF-8 flag from the perl string, so the LWP::UserAgent can insert
|
|
||||||
# the UTF-8 encoded string into the request as a byte stream.
|
|
||||||
Slic3r::path_to_filename_raw($self->{send_gcode_file})
|
|
||||||
],
|
|
||||||
print => $self->{send_gcode_file_print} ? 1 : 0,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
$self->statusbar->StopBusy;
|
|
||||||
|
|
||||||
if ($res->is_success) {
|
|
||||||
$self->statusbar->SetStatusText("G-code file successfully uploaded to the OctoPrint server");
|
|
||||||
} else {
|
|
||||||
my $message = "Error while uploading to the OctoPrint server: " . $res->status_line;
|
|
||||||
Slic3r::GUI::show_error($self, $message);
|
|
||||||
$self->statusbar->SetStatusText($message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub export_stl {
|
sub export_stl {
|
||||||
|
@ -1534,7 +1502,7 @@ sub export_stl {
|
||||||
my $output_file = $self->_get_export_file('STL') or return;
|
my $output_file = $self->_get_export_file('STL') or return;
|
||||||
# Store a binary STL.
|
# Store a binary STL.
|
||||||
$self->{model}->store_stl($output_file, 1);
|
$self->{model}->store_stl($output_file, 1);
|
||||||
$self->statusbar->SetStatusText("STL file exported to $output_file");
|
$self->statusbar->SetStatusText(L("STL file exported to ").$output_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub reload_from_disk {
|
sub reload_from_disk {
|
||||||
|
@ -1576,7 +1544,7 @@ sub export_object_stl {
|
||||||
# Ask user for a file name to write into.
|
# Ask user for a file name to write into.
|
||||||
my $output_file = $self->_get_export_file('STL') or return;
|
my $output_file = $self->_get_export_file('STL') or return;
|
||||||
$model_object->mesh->write_binary($output_file);
|
$model_object->mesh->write_binary($output_file);
|
||||||
$self->statusbar->SetStatusText("STL file exported to $output_file");
|
$self->statusbar->SetStatusText(L("STL file exported to ").$output_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub export_amf {
|
sub export_amf {
|
||||||
|
@ -1587,11 +1555,11 @@ sub export_amf {
|
||||||
my $res = $self->{model}->store_amf($output_file, $self->{print});
|
my $res = $self->{model}->store_amf($output_file, $self->{print});
|
||||||
if ($res)
|
if ($res)
|
||||||
{
|
{
|
||||||
$self->statusbar->SetStatusText("AMF file exported to $output_file");
|
$self->statusbar->SetStatusText(L("AMF file exported to ").$output_file);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$self->statusbar->SetStatusText("Error exporting AMF file $output_file");
|
$self->statusbar->SetStatusText(L("Error exporting AMF file ").$output_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1603,11 +1571,11 @@ sub export_3mf {
|
||||||
my $res = $self->{model}->store_3mf($output_file, $self->{print});
|
my $res = $self->{model}->store_3mf($output_file, $self->{print});
|
||||||
if ($res)
|
if ($res)
|
||||||
{
|
{
|
||||||
$self->statusbar->SetStatusText("3MF file exported to $output_file");
|
$self->statusbar->SetStatusText(L("3MF file exported to ").$output_file);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$self->statusbar->SetStatusText("Error exporting 3MF file $output_file");
|
$self->statusbar->SetStatusText(L("Error exporting 3MF file ").$output_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1616,23 +1584,32 @@ sub export_3mf {
|
||||||
sub _get_export_file {
|
sub _get_export_file {
|
||||||
my ($self, $format) = @_;
|
my ($self, $format) = @_;
|
||||||
my $suffix = '';
|
my $suffix = '';
|
||||||
|
my $wildcard = 'known';
|
||||||
if ($format eq 'STL')
|
if ($format eq 'STL')
|
||||||
{
|
{
|
||||||
$suffix = '.stl';
|
$suffix = '.stl';
|
||||||
|
$wildcard = 'stl';
|
||||||
}
|
}
|
||||||
elsif ($format eq 'AMF')
|
elsif ($format eq 'AMF')
|
||||||
{
|
{
|
||||||
$suffix = '.zip.amf';
|
if (&Wx::wxMAC) {
|
||||||
|
# It seems that MacOS does not like double extension
|
||||||
|
$suffix = '.amf';
|
||||||
|
} else {
|
||||||
|
$suffix = '.zip.amf';
|
||||||
|
}
|
||||||
|
$wildcard = 'amf';
|
||||||
}
|
}
|
||||||
elsif ($format eq '3MF')
|
elsif ($format eq '3MF')
|
||||||
{
|
{
|
||||||
$suffix = '.3mf';
|
$suffix = '.3mf';
|
||||||
|
$wildcard = 'threemf';
|
||||||
}
|
}
|
||||||
my $output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
|
my $output_file = eval { $self->{print}->output_filepath($main::opt{output} // '') };
|
||||||
Slic3r::GUI::catch_error($self) and return undef;
|
Slic3r::GUI::catch_error($self) and return undef;
|
||||||
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/;
|
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/;
|
||||||
my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file),
|
my $dlg = Wx::FileDialog->new($self, L("Save ").$format.L(" file as:"), dirname($output_file),
|
||||||
basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
basename($output_file), &Slic3r::GUI::FILE_WILDCARDS->{$wildcard}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
if ($dlg->ShowModal != wxID_OK) {
|
if ($dlg->ShowModal != wxID_OK) {
|
||||||
$dlg->Destroy;
|
$dlg->Destroy;
|
||||||
return undef;
|
return undef;
|
||||||
|
@ -1834,7 +1811,7 @@ sub object_cut_dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$Slic3r::GUI::have_OpenGL) {
|
if (!$Slic3r::GUI::have_OpenGL) {
|
||||||
Slic3r::GUI::show_error($self, "Please install the OpenGL modules to use this feature (see build instructions).");
|
Slic3r::GUI::show_error($self, L("Please install the OpenGL modules to use this feature (see build instructions)."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1944,19 +1921,19 @@ sub selection_changed {
|
||||||
|
|
||||||
if (my $stats = $model_object->mesh_stats) {
|
if (my $stats = $model_object->mesh_stats) {
|
||||||
$self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * ($model_instance->scaling_factor**3)));
|
$self->{object_info_volume}->SetLabel(sprintf('%.2f', $stats->{volume} * ($model_instance->scaling_factor**3)));
|
||||||
$self->{object_info_facets}->SetLabel(sprintf('%d (%d shells)', $model_object->facets_count, $stats->{number_of_parts}));
|
$self->{object_info_facets}->SetLabel(sprintf(L('%d (%d shells)'), $model_object->facets_count, $stats->{number_of_parts}));
|
||||||
if (my $errors = sum(@$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)})) {
|
if (my $errors = sum(@$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)})) {
|
||||||
$self->{object_info_manifold}->SetLabel(sprintf("Auto-repaired (%d errors)", $errors));
|
$self->{object_info_manifold}->SetLabel(sprintf(L("Auto-repaired (%d errors)"), $errors));
|
||||||
$self->{object_info_manifold_warning_icon}->Show;
|
$self->{object_info_manifold_warning_icon}->Show;
|
||||||
|
|
||||||
# we don't show normals_fixed because we never provide normals
|
# we don't show normals_fixed because we never provide normals
|
||||||
# to admesh, so it generates normals for all facets
|
# to admesh, so it generates normals for all facets
|
||||||
my $message = sprintf '%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d facets reversed, %d backwards edges',
|
my $message = sprintf L('%d degenerate facets, %d edges fixed, %d facets removed, %d facets added, %d facets reversed, %d backwards edges'),
|
||||||
@$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)};
|
@$stats{qw(degenerate_facets edges_fixed facets_removed facets_added facets_reversed backwards_edges)};
|
||||||
$self->{object_info_manifold}->SetToolTipString($message);
|
$self->{object_info_manifold}->SetToolTipString($message);
|
||||||
$self->{object_info_manifold_warning_icon}->SetToolTipString($message);
|
$self->{object_info_manifold_warning_icon}->SetToolTipString($message);
|
||||||
} else {
|
} else {
|
||||||
$self->{object_info_manifold}->SetLabel("Yes");
|
$self->{object_info_manifold}->SetLabel(L("Yes"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$self->{object_info_facets}->SetLabel($object->facets);
|
$self->{object_info_facets}->SetLabel($object->facets);
|
||||||
|
@ -2007,99 +1984,99 @@ sub object_menu {
|
||||||
my $frame = $self->GetFrame;
|
my $frame = $self->GetFrame;
|
||||||
my $menu = Wx::Menu->new;
|
my $menu = Wx::Menu->new;
|
||||||
my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
|
my $accel = ($^O eq 'MSWin32') ? sub { $_[0] . "\t\xA0" . $_[1] } : sub { $_[0] };
|
||||||
$frame->_append_menu_item($menu, $accel->('Delete', 'Del'), 'Remove the selected object', sub {
|
$frame->_append_menu_item($menu, $accel->(L('Delete'), 'Del'), L('Remove the selected object'), sub {
|
||||||
$self->remove;
|
$self->remove;
|
||||||
}, undef, 'brick_delete.png');
|
}, undef, 'brick_delete.png');
|
||||||
$frame->_append_menu_item($menu, $accel->('Increase copies', '+'), 'Place one more copy of the selected object', sub {
|
$frame->_append_menu_item($menu, $accel->(L('Increase copies'), '+'), L('Place one more copy of the selected object'), sub {
|
||||||
$self->increase;
|
$self->increase;
|
||||||
}, undef, 'add.png');
|
}, undef, 'add.png');
|
||||||
$frame->_append_menu_item($menu, $accel->('Decrease copies', '-'), 'Remove one copy of the selected object', sub {
|
$frame->_append_menu_item($menu, $accel->(L('Decrease copies'), '-'), L('Remove one copy of the selected object'), sub {
|
||||||
$self->decrease;
|
$self->decrease;
|
||||||
}, undef, 'delete.png');
|
}, undef, 'delete.png');
|
||||||
$frame->_append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub {
|
$frame->_append_menu_item($menu, L("Set number of copies…"), L('Change the number of copies of the selected object'), sub {
|
||||||
$self->set_number_of_copies;
|
$self->set_number_of_copies;
|
||||||
}, undef, 'textfield.png');
|
}, undef, 'textfield.png');
|
||||||
$menu->AppendSeparator();
|
$menu->AppendSeparator();
|
||||||
$frame->_append_menu_item($menu, $accel->('Rotate 45° clockwise', 'l'), 'Rotate the selected object by 45° clockwise', sub {
|
$frame->_append_menu_item($menu, $accel->(L('Rotate 45° clockwise'), 'l'), L('Rotate the selected object by 45° clockwise'), sub {
|
||||||
$self->rotate(-45, Z, 'relative');
|
$self->rotate(-45, Z, 'relative');
|
||||||
}, undef, 'arrow_rotate_clockwise.png');
|
}, undef, 'arrow_rotate_clockwise.png');
|
||||||
$frame->_append_menu_item($menu, $accel->('Rotate 45° counter-clockwise', 'r'), 'Rotate the selected object by 45° counter-clockwise', sub {
|
$frame->_append_menu_item($menu, $accel->(L('Rotate 45° counter-clockwise'), 'r'), L('Rotate the selected object by 45° counter-clockwise'), sub {
|
||||||
$self->rotate(+45, Z, 'relative');
|
$self->rotate(+45, Z, 'relative');
|
||||||
}, undef, 'arrow_rotate_anticlockwise.png');
|
}, undef, 'arrow_rotate_anticlockwise.png');
|
||||||
|
|
||||||
my $rotateMenu = Wx::Menu->new;
|
my $rotateMenu = Wx::Menu->new;
|
||||||
my $rotateMenuItem = $menu->AppendSubMenu($rotateMenu, "Rotate", 'Rotate the selected object by an arbitrary angle');
|
my $rotateMenuItem = $menu->AppendSubMenu($rotateMenu, L("Rotate"), L('Rotate the selected object by an arbitrary angle'));
|
||||||
$frame->_set_menu_item_icon($rotateMenuItem, 'textfield.png');
|
$frame->_set_menu_item_icon($rotateMenuItem, 'textfield.png');
|
||||||
$frame->_append_menu_item($rotateMenu, "Around X axis…", 'Rotate the selected object by an arbitrary angle around X axis', sub {
|
$frame->_append_menu_item($rotateMenu, L("Around X axis…"), L('Rotate the selected object by an arbitrary angle around X axis'), sub {
|
||||||
$self->rotate(undef, X);
|
$self->rotate(undef, X);
|
||||||
}, undef, 'bullet_red.png');
|
}, undef, 'bullet_red.png');
|
||||||
$frame->_append_menu_item($rotateMenu, "Around Y axis…", 'Rotate the selected object by an arbitrary angle around Y axis', sub {
|
$frame->_append_menu_item($rotateMenu, L("Around Y axis…"), L('Rotate the selected object by an arbitrary angle around Y axis'), sub {
|
||||||
$self->rotate(undef, Y);
|
$self->rotate(undef, Y);
|
||||||
}, undef, 'bullet_green.png');
|
}, undef, 'bullet_green.png');
|
||||||
$frame->_append_menu_item($rotateMenu, "Around Z axis…", 'Rotate the selected object by an arbitrary angle around Z axis', sub {
|
$frame->_append_menu_item($rotateMenu, L("Around Z axis…"), L('Rotate the selected object by an arbitrary angle around Z axis'), sub {
|
||||||
$self->rotate(undef, Z);
|
$self->rotate(undef, Z);
|
||||||
}, undef, 'bullet_blue.png');
|
}, undef, 'bullet_blue.png');
|
||||||
|
|
||||||
my $mirrorMenu = Wx::Menu->new;
|
my $mirrorMenu = Wx::Menu->new;
|
||||||
my $mirrorMenuItem = $menu->AppendSubMenu($mirrorMenu, "Mirror", 'Mirror the selected object');
|
my $mirrorMenuItem = $menu->AppendSubMenu($mirrorMenu, L("Mirror"), L('Mirror the selected object'));
|
||||||
$frame->_set_menu_item_icon($mirrorMenuItem, 'shape_flip_horizontal.png');
|
$frame->_set_menu_item_icon($mirrorMenuItem, 'shape_flip_horizontal.png');
|
||||||
$frame->_append_menu_item($mirrorMenu, "Along X axis…", 'Mirror the selected object along the X axis', sub {
|
$frame->_append_menu_item($mirrorMenu, L("Along X axis…"), L('Mirror the selected object along the X axis'), sub {
|
||||||
$self->mirror(X);
|
$self->mirror(X);
|
||||||
}, undef, 'bullet_red.png');
|
}, undef, 'bullet_red.png');
|
||||||
$frame->_append_menu_item($mirrorMenu, "Along Y axis…", 'Mirror the selected object along the Y axis', sub {
|
$frame->_append_menu_item($mirrorMenu, L("Along Y axis…"), L('Mirror the selected object along the Y axis'), sub {
|
||||||
$self->mirror(Y);
|
$self->mirror(Y);
|
||||||
}, undef, 'bullet_green.png');
|
}, undef, 'bullet_green.png');
|
||||||
$frame->_append_menu_item($mirrorMenu, "Along Z axis…", 'Mirror the selected object along the Z axis', sub {
|
$frame->_append_menu_item($mirrorMenu, L("Along Z axis…"), L('Mirror the selected object along the Z axis'), sub {
|
||||||
$self->mirror(Z);
|
$self->mirror(Z);
|
||||||
}, undef, 'bullet_blue.png');
|
}, undef, 'bullet_blue.png');
|
||||||
|
|
||||||
my $scaleMenu = Wx::Menu->new;
|
my $scaleMenu = Wx::Menu->new;
|
||||||
my $scaleMenuItem = $menu->AppendSubMenu($scaleMenu, "Scale", 'Scale the selected object along a single axis');
|
my $scaleMenuItem = $menu->AppendSubMenu($scaleMenu, L("Scale"), L('Scale the selected object along a single axis'));
|
||||||
$frame->_set_menu_item_icon($scaleMenuItem, 'arrow_out.png');
|
$frame->_set_menu_item_icon($scaleMenuItem, 'arrow_out.png');
|
||||||
$frame->_append_menu_item($scaleMenu, $accel->('Uniformly…', 's'), 'Scale the selected object along the XYZ axes', sub {
|
$frame->_append_menu_item($scaleMenu, $accel->(L('Uniformly…'), 's'), L('Scale the selected object along the XYZ axes'), sub {
|
||||||
$self->changescale(undef);
|
$self->changescale(undef);
|
||||||
});
|
});
|
||||||
$frame->_append_menu_item($scaleMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
|
$frame->_append_menu_item($scaleMenu, L("Along X axis…"), L('Scale the selected object along the X axis'), sub {
|
||||||
$self->changescale(X);
|
$self->changescale(X);
|
||||||
}, undef, 'bullet_red.png');
|
}, undef, 'bullet_red.png');
|
||||||
$frame->_append_menu_item($scaleMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
|
$frame->_append_menu_item($scaleMenu, L("Along Y axis…"), L('Scale the selected object along the Y axis'), sub {
|
||||||
$self->changescale(Y);
|
$self->changescale(Y);
|
||||||
}, undef, 'bullet_green.png');
|
}, undef, 'bullet_green.png');
|
||||||
$frame->_append_menu_item($scaleMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub {
|
$frame->_append_menu_item($scaleMenu, L("Along Z axis…"), L('Scale the selected object along the Z axis'), sub {
|
||||||
$self->changescale(Z);
|
$self->changescale(Z);
|
||||||
}, undef, 'bullet_blue.png');
|
}, undef, 'bullet_blue.png');
|
||||||
|
|
||||||
my $scaleToSizeMenu = Wx::Menu->new;
|
my $scaleToSizeMenu = Wx::Menu->new;
|
||||||
my $scaleToSizeMenuItem = $menu->AppendSubMenu($scaleToSizeMenu, "Scale to size", 'Scale the selected object along a single axis');
|
my $scaleToSizeMenuItem = $menu->AppendSubMenu($scaleToSizeMenu, L("Scale to size"), L('Scale the selected object along a single axis'));
|
||||||
$frame->_set_menu_item_icon($scaleToSizeMenuItem, 'arrow_out.png');
|
$frame->_set_menu_item_icon($scaleToSizeMenuItem, 'arrow_out.png');
|
||||||
$frame->_append_menu_item($scaleToSizeMenu, "Uniformly…", 'Scale the selected object along the XYZ axes', sub {
|
$frame->_append_menu_item($scaleToSizeMenu, L("Uniformly…"), L('Scale the selected object along the XYZ axes'), sub {
|
||||||
$self->changescale(undef, 1);
|
$self->changescale(undef, 1);
|
||||||
});
|
});
|
||||||
$frame->_append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
|
$frame->_append_menu_item($scaleToSizeMenu, L("Along X axis…"), L('Scale the selected object along the X axis'), sub {
|
||||||
$self->changescale(X, 1);
|
$self->changescale(X, 1);
|
||||||
}, undef, 'bullet_red.png');
|
}, undef, 'bullet_red.png');
|
||||||
$frame->_append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
|
$frame->_append_menu_item($scaleToSizeMenu, L("Along Y axis…"), L('Scale the selected object along the Y axis'), sub {
|
||||||
$self->changescale(Y, 1);
|
$self->changescale(Y, 1);
|
||||||
}, undef, 'bullet_green.png');
|
}, undef, 'bullet_green.png');
|
||||||
$frame->_append_menu_item($scaleToSizeMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub {
|
$frame->_append_menu_item($scaleToSizeMenu, L("Along Z axis…"), L('Scale the selected object along the Z axis'), sub {
|
||||||
$self->changescale(Z, 1);
|
$self->changescale(Z, 1);
|
||||||
}, undef, 'bullet_blue.png');
|
}, undef, 'bullet_blue.png');
|
||||||
|
|
||||||
$frame->_append_menu_item($menu, "Split", 'Split the selected object into individual parts', sub {
|
$frame->_append_menu_item($menu, L("Split"), L('Split the selected object into individual parts'), sub {
|
||||||
$self->split_object;
|
$self->split_object;
|
||||||
}, undef, 'shape_ungroup.png');
|
}, undef, 'shape_ungroup.png');
|
||||||
$frame->_append_menu_item($menu, "Cut…", 'Open the 3D cutting tool', sub {
|
$frame->_append_menu_item($menu, L("Cut…"), L('Open the 3D cutting tool'), sub {
|
||||||
$self->object_cut_dialog;
|
$self->object_cut_dialog;
|
||||||
}, undef, 'package.png');
|
}, undef, 'package.png');
|
||||||
$menu->AppendSeparator();
|
$menu->AppendSeparator();
|
||||||
$frame->_append_menu_item($menu, "Settings…", 'Open the object editor dialog', sub {
|
$frame->_append_menu_item($menu, L("Settings…"), L('Open the object editor dialog'), sub {
|
||||||
$self->object_settings_dialog;
|
$self->object_settings_dialog;
|
||||||
}, undef, 'cog.png');
|
}, undef, 'cog.png');
|
||||||
$menu->AppendSeparator();
|
$menu->AppendSeparator();
|
||||||
$frame->_append_menu_item($menu, "Reload from Disk", 'Reload the selected file from Disk', sub {
|
$frame->_append_menu_item($menu, L("Reload from Disk"), L('Reload the selected file from Disk'), sub {
|
||||||
$self->reload_from_disk;
|
$self->reload_from_disk;
|
||||||
}, undef, 'arrow_refresh.png');
|
}, undef, 'arrow_refresh.png');
|
||||||
$frame->_append_menu_item($menu, "Export object as STL…", 'Export this single object as STL file', sub {
|
$frame->_append_menu_item($menu, L("Export object as STL…"), L('Export this single object as STL file'), sub {
|
||||||
$self->export_object_stl;
|
$self->export_object_stl;
|
||||||
}, undef, 'brick_go.png');
|
}, undef, 'brick_go.png');
|
||||||
|
|
||||||
|
@ -2110,8 +2087,8 @@ sub object_menu {
|
||||||
sub select_view {
|
sub select_view {
|
||||||
my ($self, $direction) = @_;
|
my ($self, $direction) = @_;
|
||||||
my $idx_page = $self->{preview_notebook}->GetSelection;
|
my $idx_page = $self->{preview_notebook}->GetSelection;
|
||||||
my $page = ($idx_page == &Wx::wxNOT_FOUND) ? '3D' : $self->{preview_notebook}->GetPageText($idx_page);
|
my $page = ($idx_page == &Wx::wxNOT_FOUND) ? L('3D') : $self->{preview_notebook}->GetPageText($idx_page);
|
||||||
if ($page eq 'Preview') {
|
if ($page eq L('Preview')) {
|
||||||
$self->{preview3D}->canvas->select_view($direction);
|
$self->{preview3D}->canvas->select_view($direction);
|
||||||
$self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas);
|
$self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -13,6 +13,8 @@ use Wx qw(wxTheApp :misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL);
|
||||||
use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE);
|
use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE);
|
||||||
use base 'Wx::Panel';
|
use base 'Wx::Panel';
|
||||||
|
|
||||||
|
use Wx::Locale gettext => 'L';
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
my ($parent, $size, $objects, $model, $config) = @_;
|
my ($parent, $size, $objects, $model, $config) = @_;
|
||||||
|
@ -126,8 +128,8 @@ sub repaint {
|
||||||
$dc->SetFont(Wx::Font->new(14, wxDEFAULT, wxNORMAL, wxNORMAL));
|
$dc->SetFont(Wx::Font->new(14, wxDEFAULT, wxNORMAL, wxNORMAL));
|
||||||
$dc->DrawLabel(
|
$dc->DrawLabel(
|
||||||
join('-', +(localtime)[3,4]) eq '13-8'
|
join('-', +(localtime)[3,4]) eq '13-8'
|
||||||
? 'What do you want to print today? ™' # Sept. 13, 2006. The first part ever printed by a RepRap to make another RepRap.
|
? L('What do you want to print today? ™') # Sept. 13, 2006. The first part ever printed by a RepRap to make another RepRap.
|
||||||
: 'Drag your objects here',
|
: L('Drag your objects here'),
|
||||||
Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL);
|
Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -338,7 +338,6 @@ sub Render {
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
if (!$self->GetParent->enabled || !$self->layers) {
|
if (!$self->GetParent->enabled || !$self->layers) {
|
||||||
glFlush();
|
|
||||||
$self->SwapBuffers;
|
$self->SwapBuffers;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -486,7 +485,6 @@ sub Render {
|
||||||
}
|
}
|
||||||
|
|
||||||
gluDeleteTess($tess) if $tess;
|
gluDeleteTess($tess) if $tess;
|
||||||
glFlush();
|
|
||||||
$self->SwapBuffers;
|
$self->SwapBuffers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE wxCB_READONLY);
|
||||||
use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX EVT_CHOICE EVT_CHECKLISTBOX);
|
use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX EVT_CHOICE EVT_CHECKLISTBOX);
|
||||||
use base qw(Wx::Panel Class::Accessor);
|
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 auto_zoom));
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
|
@ -58,34 +60,42 @@ sub new {
|
||||||
$z_label_high->SetFont($Slic3r::GUI::small_font);
|
$z_label_high->SetFont($Slic3r::GUI::small_font);
|
||||||
|
|
||||||
$self->single_layer(0);
|
$self->single_layer(0);
|
||||||
my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, "1 Layer");
|
my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, L("1 Layer"));
|
||||||
|
|
||||||
my $label_view_type = $self->{label_view_type} = Wx::StaticText->new($self, -1, "View");
|
my $label_view_type = $self->{label_view_type} = Wx::StaticText->new($self, -1, L("View"));
|
||||||
|
|
||||||
my $choice_view_type = $self->{choice_view_type} = Wx::Choice->new($self, -1);
|
my $choice_view_type = $self->{choice_view_type} = Wx::Choice->new($self, -1);
|
||||||
$choice_view_type->Append("Feature type");
|
$choice_view_type->Append(L("Feature type"));
|
||||||
$choice_view_type->Append("Height");
|
$choice_view_type->Append(L("Height"));
|
||||||
$choice_view_type->Append("Width");
|
$choice_view_type->Append(L("Width"));
|
||||||
$choice_view_type->Append("Speed");
|
$choice_view_type->Append(L("Speed"));
|
||||||
$choice_view_type->Append("Tool");
|
$choice_view_type->Append(L("Tool"));
|
||||||
$choice_view_type->SetSelection(0);
|
$choice_view_type->SetSelection(0);
|
||||||
|
|
||||||
my $label_show_features = $self->{label_show_features} = Wx::StaticText->new($self, -1, "Show");
|
my $label_show_features = $self->{label_show_features} = Wx::StaticText->new($self, -1, L("Show"));
|
||||||
|
|
||||||
my $combochecklist_features = $self->{combochecklist_features} = Wx::ComboCtrl->new();
|
my $combochecklist_features = $self->{combochecklist_features} = Wx::ComboCtrl->new();
|
||||||
$combochecklist_features->Create($self, -1, "Feature types", wxDefaultPosition, [200, -1], wxCB_READONLY);
|
$combochecklist_features->Create($self, -1, L("Feature types"), wxDefaultPosition, [200, -1], wxCB_READONLY);
|
||||||
#FIXME If the following line is removed, the combo box popup list will not react to mouse clicks.
|
my $feature_text = L("Feature types");
|
||||||
# On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10.
|
my $feature_items = L("Perimeter")."|"
|
||||||
$combochecklist_features->UseAltPopupWindow();
|
.L("External perimeter")."|"
|
||||||
$combochecklist_features->EnablePopupAnimation(0);
|
.L("Overhang perimeter")."|"
|
||||||
my $feature_text = "Feature types";
|
.L("Internal infill")."|"
|
||||||
my $feature_items = "Perimeter|External perimeter|Overhang perimeter|Internal infill|Solid infill|Top solid infill|Bridge infill|Gap fill|Skirt|Support material|Support material interface|Wipe tower";
|
.L("Solid infill")."|"
|
||||||
|
.L("Top solid infill")."|"
|
||||||
|
.L("Bridge infill")."|"
|
||||||
|
.L("Gap fill")."|"
|
||||||
|
.L("Skirt")."|"
|
||||||
|
.L("Support material")."|"
|
||||||
|
.L("Support material interface")."|"
|
||||||
|
.L("Wipe tower")."|"
|
||||||
|
.L("Custom");
|
||||||
Slic3r::GUI::create_combochecklist($combochecklist_features, $feature_text, $feature_items, 1);
|
Slic3r::GUI::create_combochecklist($combochecklist_features, $feature_text, $feature_items, 1);
|
||||||
|
|
||||||
my $checkbox_travel = $self->{checkbox_travel} = Wx::CheckBox->new($self, -1, "Travel");
|
my $checkbox_travel = $self->{checkbox_travel} = Wx::CheckBox->new($self, -1, L("Travel"));
|
||||||
my $checkbox_retractions = $self->{checkbox_retractions} = Wx::CheckBox->new($self, -1, "Retractions");
|
my $checkbox_retractions = $self->{checkbox_retractions} = Wx::CheckBox->new($self, -1, L("Retractions"));
|
||||||
my $checkbox_unretractions = $self->{checkbox_unretractions} = Wx::CheckBox->new($self, -1, "Unretractions");
|
my $checkbox_unretractions = $self->{checkbox_unretractions} = Wx::CheckBox->new($self, -1, L("Unretractions"));
|
||||||
my $checkbox_shells = $self->{checkbox_shells} = Wx::CheckBox->new($self, -1, "Shells");
|
my $checkbox_shells = $self->{checkbox_shells} = Wx::CheckBox->new($self, -1, L("Shells"));
|
||||||
|
|
||||||
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
|
@ -255,6 +265,7 @@ sub new {
|
||||||
'Support material' => '00FF00',
|
'Support material' => '00FF00',
|
||||||
'Support material interface' => '008000',
|
'Support material interface' => '008000',
|
||||||
'Wipe tower' => 'B3E3AB',
|
'Wipe tower' => 'B3E3AB',
|
||||||
|
'Custom' => 'FFFF00',
|
||||||
);
|
);
|
||||||
$self->gcode_preview_data->set_extrusion_paths_colors(\@extrusion_roles_colors);
|
$self->gcode_preview_data->set_extrusion_paths_colors(\@extrusion_roles_colors);
|
||||||
|
|
||||||
|
@ -322,31 +333,9 @@ sub load_print {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $z_idx_low = $self->slider_low->GetValue;
|
# used to set the sliders to the extremes of the current zs range
|
||||||
my $z_idx_high = $self->slider_high->GetValue;
|
$self->{force_sliders_full_range} = 0;
|
||||||
$self->enabled(1);
|
|
||||||
$self->slider_low->SetRange(0, $n_layers - 1);
|
|
||||||
$self->slider_high->SetRange(0, $n_layers - 1);
|
|
||||||
if ($z_idx_high < $n_layers && ($self->single_layer || $z_idx_high != 0)) {
|
|
||||||
# use $z_idx
|
|
||||||
} else {
|
|
||||||
# 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) {
|
|
||||||
$z_idx_low = 0;
|
|
||||||
}
|
|
||||||
$self->slider_low->SetValue($z_idx_low);
|
|
||||||
$self->slider_high->SetValue($z_idx_high);
|
|
||||||
$self->slider_low->Show;
|
|
||||||
$self->slider_high->Show;
|
|
||||||
$self->Layout;
|
|
||||||
|
|
||||||
if ($self->{preferred_color_mode} eq 'tool_or_feature') {
|
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.
|
# 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.
|
# Color by feature if it is a single extruder print.
|
||||||
|
@ -384,16 +373,58 @@ sub load_print {
|
||||||
}
|
}
|
||||||
$self->show_hide_ui_elements('simple');
|
$self->show_hide_ui_elements('simple');
|
||||||
} else {
|
} 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->canvas->load_gcode_preview($self->print, $self->gcode_preview_data, \@colors);
|
||||||
$self->show_hide_ui_elements('full');
|
$self->show_hide_ui_elements('full');
|
||||||
|
|
||||||
|
# recalculates zs and update sliders accordingly
|
||||||
|
$self->{layers_z} = $self->canvas->get_current_print_zs();
|
||||||
|
$n_layers = scalar(@{$self->{layers_z}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$self->update_sliders($n_layers);
|
||||||
|
|
||||||
if ($self->auto_zoom) {
|
if ($self->auto_zoom) {
|
||||||
$self->canvas->zoom_to_volumes;
|
$self->canvas->zoom_to_volumes;
|
||||||
}
|
}
|
||||||
$self->_loaded(1);
|
$self->_loaded(1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub update_sliders
|
||||||
|
{
|
||||||
|
my ($self, $n_layers) = @_;
|
||||||
|
|
||||||
|
my $z_idx_low = $self->slider_low->GetValue;
|
||||||
|
my $z_idx_high = $self->slider_high->GetValue;
|
||||||
|
$self->enabled(1);
|
||||||
|
$self->slider_low->SetRange(0, $n_layers - 1);
|
||||||
|
$self->slider_high->SetRange(0, $n_layers - 1);
|
||||||
|
|
||||||
|
if ($self->{force_sliders_full_range}) {
|
||||||
|
$z_idx_low = 0;
|
||||||
|
$z_idx_high = $n_layers - 1;
|
||||||
|
} elsif ($z_idx_high < $n_layers && ($self->single_layer || $z_idx_high != 0)) {
|
||||||
|
# use $z_idx
|
||||||
|
} else {
|
||||||
|
# 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) {
|
||||||
|
$z_idx_low = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->slider_low->SetValue($z_idx_low);
|
||||||
|
$self->slider_high->SetValue($z_idx_high);
|
||||||
|
$self->slider_low->Show;
|
||||||
|
$self->slider_high->Show;
|
||||||
$self->set_z_range($self->{layers_z}[$z_idx_low], $self->{layers_z}[$z_idx_high]);
|
$self->set_z_range($self->{layers_z}[$z_idx_low], $self->{layers_z}[$z_idx_high]);
|
||||||
|
$self->Layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub set_z_range
|
sub set_z_range
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
# Preferences dialog, opens from Menu: File->Preferences
|
|
||||||
|
|
||||||
package Slic3r::GUI::Preferences;
|
|
||||||
use Wx qw(:dialog :id :misc :sizer :systemsettings wxTheApp);
|
|
||||||
use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER);
|
|
||||||
use base 'Wx::Dialog';
|
|
||||||
|
|
||||||
sub new {
|
|
||||||
my ($class, $parent) = @_;
|
|
||||||
my $self = $class->SUPER::new($parent, -1, "Preferences", wxDefaultPosition, wxDefaultSize);
|
|
||||||
$self->{values} = {};
|
|
||||||
|
|
||||||
my $app_config = wxTheApp->{app_config};
|
|
||||||
my $optgroup;
|
|
||||||
$optgroup = Slic3r::GUI::OptionsGroup->new(
|
|
||||||
parent => $self,
|
|
||||||
title => 'General',
|
|
||||||
on_change => sub {
|
|
||||||
my ($opt_id) = @_;
|
|
||||||
$self->{values}{$opt_id} = $optgroup->get_value($opt_id);
|
|
||||||
},
|
|
||||||
label_width => 200,
|
|
||||||
);
|
|
||||||
# $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
|
||||||
# opt_id => 'version_check',
|
|
||||||
# type => 'bool',
|
|
||||||
# label => 'Check for updates',
|
|
||||||
# tooltip => 'If this is enabled, Slic3r will check for updates daily and display a reminder if a newer version is available.',
|
|
||||||
# default => $app_config->get("version_check") // 1,
|
|
||||||
# readonly => !wxTheApp->have_version_check,
|
|
||||||
# ));
|
|
||||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
|
||||||
opt_id => 'remember_output_path',
|
|
||||||
type => 'bool',
|
|
||||||
label => 'Remember output directory',
|
|
||||||
tooltip => 'If this is enabled, Slic3r will prompt the last output directory instead of the one containing the input files.',
|
|
||||||
default => $app_config->get("remember_output_path"),
|
|
||||||
));
|
|
||||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
|
||||||
opt_id => 'autocenter',
|
|
||||||
type => 'bool',
|
|
||||||
label => 'Auto-center parts',
|
|
||||||
tooltip => 'If this is enabled, Slic3r will auto-center objects around the print bed center.',
|
|
||||||
default => $app_config->get("autocenter"),
|
|
||||||
));
|
|
||||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
|
||||||
opt_id => 'background_processing',
|
|
||||||
type => 'bool',
|
|
||||||
label => 'Background processing',
|
|
||||||
tooltip => 'If this is enabled, Slic3r will pre-process objects as soon as they\'re loaded in order to save time when exporting G-code.',
|
|
||||||
default => $app_config->get("background_processing"),
|
|
||||||
));
|
|
||||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
|
||||||
opt_id => 'no_controller',
|
|
||||||
type => 'bool',
|
|
||||||
label => 'Disable USB/serial connection',
|
|
||||||
tooltip => 'Disable communication with the printer over a serial / USB cable. This simplifies the user interface in case the printer is never attached to the computer.',
|
|
||||||
default => $app_config->get("no_controller"),
|
|
||||||
));
|
|
||||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
|
||||||
opt_id => 'no_defaults',
|
|
||||||
type => 'bool',
|
|
||||||
label => 'Suppress "- default -" presets',
|
|
||||||
tooltip => 'Suppress "- default -" presets in the Print / Filament / Printer selections once there are any other valid presets available.',
|
|
||||||
default => $app_config->get("no_defaults"),
|
|
||||||
));
|
|
||||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
|
||||||
opt_id => 'show_incompatible_presets',
|
|
||||||
type => 'bool',
|
|
||||||
label => 'Show incompatible print and filament presets',
|
|
||||||
tooltip => 'When checked, the print and filament presets are shown in the preset editor even ' .
|
|
||||||
'if they are marked as incompatible with the active printer',
|
|
||||||
default => $app_config->get("show_incompatible_presets"),
|
|
||||||
));
|
|
||||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
|
||||||
opt_id => 'use_legacy_opengl',
|
|
||||||
type => 'bool',
|
|
||||||
label => 'Use legacy OpenGL 1.1 rendering',
|
|
||||||
tooltip => 'If you have rendering issues caused by a buggy OpenGL 2.0 driver, you may try to check this checkbox. This will disable the layer height editing and anti aliasing, so it is likely better to upgrade your graphics driver.',
|
|
||||||
default => $app_config->get("use_legacy_opengl"),
|
|
||||||
));
|
|
||||||
|
|
||||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
|
||||||
$sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
|
||||||
|
|
||||||
my $buttons = $self->CreateStdDialogButtonSizer(wxOK | wxCANCEL);
|
|
||||||
EVT_BUTTON($self, wxID_OK, sub { $self->_accept });
|
|
||||||
$sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
|
||||||
|
|
||||||
$self->SetSizer($sizer);
|
|
||||||
$sizer->SetSizeHints($self);
|
|
||||||
|
|
||||||
return $self;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub _accept {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
if (defined($self->{values}{no_controller}) ||
|
|
||||||
defined($self->{values}{no_defaults}) ||
|
|
||||||
defined($self->{values}{use_legacy_opengl})) {
|
|
||||||
Slic3r::GUI::warning_catcher($self)->("You need to restart Slic3r to make the changes effective.");
|
|
||||||
}
|
|
||||||
|
|
||||||
my $app_config = wxTheApp->{app_config};
|
|
||||||
$app_config->set($_, $self->{values}{$_}) for keys %{$self->{values}};
|
|
||||||
|
|
||||||
$self->EndModal(wxID_OK);
|
|
||||||
$self->Close; # needed on Linux
|
|
||||||
|
|
||||||
# Nothify the UI to update itself from the ini file.
|
|
||||||
wxTheApp->update_ui_from_settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
3883
resources/localization/Slic3rPE.pot
Normal file
3883
resources/localization/Slic3rPE.pot
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
BIN
resources/localization/de_DE/Slic3rPE.mo
Normal file
BIN
resources/localization/de_DE/Slic3rPE.mo
Normal file
Binary file not shown.
4667
resources/localization/de_DE/Slic3rPE_de.po
Normal file
4667
resources/localization/de_DE/Slic3rPE_de.po
Normal file
File diff suppressed because it is too large
Load diff
Binary file not shown.
File diff suppressed because it is too large
Load diff
BIN
resources/localization/fr_FR/Slic3rPE.mo
Normal file
BIN
resources/localization/fr_FR/Slic3rPE.mo
Normal file
Binary file not shown.
4702
resources/localization/fr_FR/Slic3rPE_fr.po
Normal file
4702
resources/localization/fr_FR/Slic3rPE_fr.po
Normal file
File diff suppressed because it is too large
Load diff
18
resources/localization/list.txt
Normal file
18
resources/localization/list.txt
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.cpp
|
||||||
|
c:\src\Slic3r\xs\src\slic3r\GUI\BedShapeDialog.hpp
|
||||||
|
c:\src\Slic3r\xs\src\slic3r\GUI\GUI.cpp
|
||||||
|
c:\src\Slic3r\xs\src\slic3r\GUI\Tab.cpp
|
||||||
|
c:\src\Slic3r\xs\src\slic3r\GUI\Tab.hpp
|
||||||
|
c:\src\Slic3r\xs\src\slic3r\GUI\Field.cpp
|
||||||
|
c:\src\Slic3r\xs\src\slic3r\GUI\OptionsGroup.cpp
|
||||||
|
c:\src\Slic3r\xs\src\slic3r\GUI\2DBed.cpp
|
||||||
|
c:\src\Slic3r\xs\src\slic3r\GUI\PresetHints.cpp
|
||||||
|
c:\src\Slic3r\xs\src\slic3r\GUI\Preferences.hpp
|
||||||
|
c:\src\Slic3r\xs\src\slic3r\GUI\Preferences.cpp
|
||||||
|
C:\src\Slic3r\xs\src\libslic3r\PrintConfig.cpp
|
||||||
|
c:\src\Slic3r\xs\src\libslic3r\GCode\PreviewData.cpp
|
||||||
|
c:\src\Slic3r\lib\Slic3r\GUI.pm
|
||||||
|
c:\src\Slic3r\lib\Slic3r\GUI\MainFrame.pm
|
||||||
|
c:\src\Slic3r\lib\Slic3r\GUI\Plater.pm
|
||||||
|
c:\src\Slic3r\lib\Slic3r\GUI\Plater\2D.pm
|
||||||
|
c:\src\Slic3r\lib\Slic3r\GUI\Plater\3DPreview.pm
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
BIN
resources/localization/zh_CN/Slic3rPE.mo
Normal file
BIN
resources/localization/zh_CN/Slic3rPE.mo
Normal file
Binary file not shown.
4468
resources/localization/zh_CN/Slic3rPE_zh.po
Normal file
4468
resources/localization/zh_CN/Slic3rPE_zh.po
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -64,6 +64,8 @@ add_library(libslic3r STATIC
|
||||||
${LIBDIR}/libslic3r/Fill/FillConcentric.hpp
|
${LIBDIR}/libslic3r/Fill/FillConcentric.hpp
|
||||||
${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp
|
${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp
|
||||||
${LIBDIR}/libslic3r/Fill/FillHoneycomb.hpp
|
${LIBDIR}/libslic3r/Fill/FillHoneycomb.hpp
|
||||||
|
${LIBDIR}/libslic3r/Fill/FillGyroid.cpp
|
||||||
|
${LIBDIR}/libslic3r/Fill/FillGyroid.hpp
|
||||||
${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp
|
${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp
|
||||||
${LIBDIR}/libslic3r/Fill/FillPlanePath.hpp
|
${LIBDIR}/libslic3r/Fill/FillPlanePath.hpp
|
||||||
${LIBDIR}/libslic3r/Fill/FillRectilinear.cpp
|
${LIBDIR}/libslic3r/Fill/FillRectilinear.cpp
|
||||||
|
@ -173,6 +175,8 @@ add_library(libslic3r_gui STATIC
|
||||||
${LIBDIR}/slic3r/GUI/3DScene.hpp
|
${LIBDIR}/slic3r/GUI/3DScene.hpp
|
||||||
${LIBDIR}/slic3r/GUI/GLShader.cpp
|
${LIBDIR}/slic3r/GUI/GLShader.cpp
|
||||||
${LIBDIR}/slic3r/GUI/GLShader.hpp
|
${LIBDIR}/slic3r/GUI/GLShader.hpp
|
||||||
|
${LIBDIR}/slic3r/GUI/Preferences.cpp
|
||||||
|
${LIBDIR}/slic3r/GUI/Preferences.hpp
|
||||||
${LIBDIR}/slic3r/GUI/Preset.cpp
|
${LIBDIR}/slic3r/GUI/Preset.cpp
|
||||||
${LIBDIR}/slic3r/GUI/Preset.hpp
|
${LIBDIR}/slic3r/GUI/Preset.hpp
|
||||||
${LIBDIR}/slic3r/GUI/PresetBundle.cpp
|
${LIBDIR}/slic3r/GUI/PresetBundle.cpp
|
||||||
|
@ -199,7 +203,12 @@ add_library(libslic3r_gui STATIC
|
||||||
${LIBDIR}/slic3r/GUI/WipeTowerDialog.hpp
|
${LIBDIR}/slic3r/GUI/WipeTowerDialog.hpp
|
||||||
${LIBDIR}/slic3r/GUI/RammingChart.cpp
|
${LIBDIR}/slic3r/GUI/RammingChart.cpp
|
||||||
${LIBDIR}/slic3r/GUI/RammingChart.hpp
|
${LIBDIR}/slic3r/GUI/RammingChart.hpp
|
||||||
|
${LIBDIR}/slic3r/Utils/Http.cpp
|
||||||
|
${LIBDIR}/slic3r/Utils/Http.hpp
|
||||||
|
${LIBDIR}/slic3r/Utils/OctoPrint.cpp
|
||||||
|
${LIBDIR}/slic3r/Utils/OctoPrint.hpp
|
||||||
|
${LIBDIR}/slic3r/Utils/Bonjour.cpp
|
||||||
|
${LIBDIR}/slic3r/Utils/Bonjour.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(admesh STATIC
|
add_library(admesh STATIC
|
||||||
|
@ -339,6 +348,7 @@ set(XS_XSP_FILES
|
||||||
${XSP_DIR}/Surface.xsp
|
${XSP_DIR}/Surface.xsp
|
||||||
${XSP_DIR}/SurfaceCollection.xsp
|
${XSP_DIR}/SurfaceCollection.xsp
|
||||||
${XSP_DIR}/TriangleMesh.xsp
|
${XSP_DIR}/TriangleMesh.xsp
|
||||||
|
${XSP_DIR}/Utils_OctoPrint.xsp
|
||||||
${XSP_DIR}/XS.xsp
|
${XSP_DIR}/XS.xsp
|
||||||
)
|
)
|
||||||
foreach (file ${XS_XSP_FILES})
|
foreach (file ${XS_XSP_FILES})
|
||||||
|
@ -476,6 +486,7 @@ if(SLIC3R_STATIC)
|
||||||
# Use boost libraries linked statically to the C++ runtime.
|
# Use boost libraries linked statically to the C++ runtime.
|
||||||
# set(Boost_USE_STATIC_RUNTIME ON)
|
# set(Boost_USE_STATIC_RUNTIME ON)
|
||||||
endif()
|
endif()
|
||||||
|
#set(Boost_DEBUG ON)
|
||||||
find_package(Boost REQUIRED COMPONENTS system filesystem thread log locale regex)
|
find_package(Boost REQUIRED COMPONENTS system filesystem thread log locale regex)
|
||||||
if(Boost_FOUND)
|
if(Boost_FOUND)
|
||||||
include_directories(${Boost_INCLUDE_DIRS})
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
|
@ -523,6 +534,27 @@ if (SLIC3R_PRUSACONTROL)
|
||||||
target_link_libraries(XS ${wxWidgets_LIBRARIES})
|
target_link_libraries(XS ${wxWidgets_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(CURL REQUIRED)
|
||||||
|
include_directories(${CURL_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(XS ${CURL_LIBRARIES})
|
||||||
|
|
||||||
|
if (SLIC3R_STATIC)
|
||||||
|
if (NOT APPLE)
|
||||||
|
# libcurl is always linked dynamically to the system libcurl on OSX.
|
||||||
|
# On other systems, libcurl is linked statically if SLIC3R_STATIC is set.
|
||||||
|
add_definitions(-DCURL_STATICLIB)
|
||||||
|
endif()
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
# As of now, our build system produces a statically linked libcurl,
|
||||||
|
# which links the OpenSSL library dynamically.
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}")
|
||||||
|
message("OpenSSL libraries: ${OPENSSL_LIBRARIES}")
|
||||||
|
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||||
|
target_link_libraries(XS ${OPENSSL_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
## OPTIONAL packages
|
## OPTIONAL packages
|
||||||
|
|
||||||
# Find eigen3 or use bundled version
|
# Find eigen3 or use bundled version
|
||||||
|
@ -597,6 +629,17 @@ elseif (NOT MSVC)
|
||||||
target_link_libraries(slic3r -lstdc++)
|
target_link_libraries(slic3r -lstdc++)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Here we associate some additional properties with the MSVC projects to enable compilation and debugging out of the box.
|
||||||
|
# It seems a props file needs to be copied to the same dir as the proj file, otherwise MSVC doesn't load it up.
|
||||||
|
# For copying, the configure_file() function seems to work much better than the file() function.
|
||||||
|
configure_file("${PROJECT_SOURCE_DIR}/cmake/msvc/xs.wperl64d.props" ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
|
||||||
|
set_target_properties(XS PROPERTIES VS_USER_PROPS "xs.wperl64d.props")
|
||||||
|
configure_file("${PROJECT_SOURCE_DIR}/cmake/msvc/slic3r.wperl64d.props" ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
|
||||||
|
set_target_properties(slic3r PROPERTIES VS_USER_PROPS "slic3r.wperl64d.props")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
install(TARGETS XS DESTINATION lib/slic3r-prusa3d/auto/Slic3r/XS)
|
install(TARGETS XS DESTINATION ${PERL_VENDORARCH}/auto/Slic3r/XS)
|
||||||
install(FILES lib/Slic3r/XS.pm DESTINATION lib/slic3r-prusa3d/Slic3r)
|
install(FILES lib/Slic3r/XS.pm DESTINATION ${PERL_VENDORLIB}/Slic3r)
|
||||||
|
|
|
@ -107,6 +107,11 @@ stl_fix_normal_directions(stl_file *stl) {
|
||||||
struct stl_normal *newn;
|
struct stl_normal *newn;
|
||||||
struct stl_normal *temp;
|
struct stl_normal *temp;
|
||||||
|
|
||||||
|
int* reversed_ids;
|
||||||
|
int reversed_count = 0;
|
||||||
|
int id;
|
||||||
|
int force_exit = 0;
|
||||||
|
|
||||||
if (stl->error) return;
|
if (stl->error) return;
|
||||||
|
|
||||||
/* Initialize linked list. */
|
/* Initialize linked list. */
|
||||||
|
@ -121,13 +126,18 @@ stl_fix_normal_directions(stl_file *stl) {
|
||||||
norm_sw = (char*)calloc(stl->stats.number_of_facets, sizeof(char));
|
norm_sw = (char*)calloc(stl->stats.number_of_facets, sizeof(char));
|
||||||
if(norm_sw == NULL) perror("stl_fix_normal_directions");
|
if(norm_sw == NULL) perror("stl_fix_normal_directions");
|
||||||
|
|
||||||
|
/* Initialize list that keeps track of reversed facets. */
|
||||||
|
reversed_ids = (int*)calloc(stl->stats.number_of_facets, sizeof(int));
|
||||||
|
if (reversed_ids == NULL) perror("stl_fix_normal_directions reversed_ids");
|
||||||
|
|
||||||
facet_num = 0;
|
facet_num = 0;
|
||||||
/* If normal vector is not within tolerance and backwards:
|
/* If normal vector is not within tolerance and backwards:
|
||||||
Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances
|
Arbitrarily starts at face 0. If this one is wrong, we're screwed. Thankfully, the chances
|
||||||
of it being wrong randomly are low if most of the triangles are right: */
|
of it being wrong randomly are low if most of the triangles are right: */
|
||||||
if(stl_check_normal_vector(stl, 0, 0) == 2)
|
if (stl_check_normal_vector(stl, 0, 0) == 2) {
|
||||||
stl_reverse_facet(stl, 0);
|
stl_reverse_facet(stl, 0);
|
||||||
|
reversed_ids[reversed_count++] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Say that we've fixed this facet: */
|
/* Say that we've fixed this facet: */
|
||||||
norm_sw[facet_num] = 1;
|
norm_sw[facet_num] = 1;
|
||||||
|
@ -140,8 +150,19 @@ stl_fix_normal_directions(stl_file *stl) {
|
||||||
/* Reverse the neighboring facets if necessary. */
|
/* Reverse the neighboring facets if necessary. */
|
||||||
if(stl->neighbors_start[facet_num].which_vertex_not[j] > 2) {
|
if(stl->neighbors_start[facet_num].which_vertex_not[j] > 2) {
|
||||||
/* If the facet has a neighbor that is -1, it means that edge isn't shared by another facet */
|
/* If the facet has a neighbor that is -1, it means that edge isn't shared by another facet */
|
||||||
if(stl->neighbors_start[facet_num].neighbor[j] != -1)
|
if(stl->neighbors_start[facet_num].neighbor[j] != -1) {
|
||||||
stl_reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]);
|
if (norm_sw[stl->neighbors_start[facet_num].neighbor[j]] == 1) {
|
||||||
|
/* trying to modify a facet already marked as fixed, revert all changes made until now and exit (fixes: #716, #574, #413, #269, #262, #259, #230, #228, #206) */
|
||||||
|
for (id = reversed_count - 1; id >= 0; --id) {
|
||||||
|
stl_reverse_facet(stl, reversed_ids[id]);
|
||||||
|
}
|
||||||
|
force_exit = 1;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
stl_reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]);
|
||||||
|
reversed_ids[reversed_count++] = stl->neighbors_start[facet_num].neighbor[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* If this edge of the facet is connected: */
|
/* If this edge of the facet is connected: */
|
||||||
if(stl->neighbors_start[facet_num].neighbor[j] != -1) {
|
if(stl->neighbors_start[facet_num].neighbor[j] != -1) {
|
||||||
|
@ -156,6 +177,10 @@ stl_fix_normal_directions(stl_file *stl) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* an error occourred, quit the for loop and exit */
|
||||||
|
if (force_exit) break;
|
||||||
|
|
||||||
/* Get next facet to fix from top of list. */
|
/* Get next facet to fix from top of list. */
|
||||||
if(head->next != tail) {
|
if(head->next != tail) {
|
||||||
facet_num = head->next->facet_num;
|
facet_num = head->next->facet_num;
|
||||||
|
@ -179,7 +204,8 @@ stl_fix_normal_directions(stl_file *stl) {
|
||||||
/* This is the first facet of the next part. */
|
/* This is the first facet of the next part. */
|
||||||
facet_num = i;
|
facet_num = i;
|
||||||
if(stl_check_normal_vector(stl, i, 0) == 2) {
|
if(stl_check_normal_vector(stl, i, 0) == 2) {
|
||||||
stl_reverse_facet(stl, i);
|
stl_reverse_facet(stl, i);
|
||||||
|
reversed_ids[reversed_count++] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
norm_sw[facet_num] = 1;
|
norm_sw[facet_num] = 1;
|
||||||
|
@ -192,6 +218,7 @@ stl_fix_normal_directions(stl_file *stl) {
|
||||||
}
|
}
|
||||||
free(head);
|
free(head);
|
||||||
free(tail);
|
free(tail);
|
||||||
|
free(reversed_ids);
|
||||||
free(norm_sw);
|
free(norm_sw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ enum ExtrusionRole {
|
||||||
erSupportMaterial,
|
erSupportMaterial,
|
||||||
erSupportMaterialInterface,
|
erSupportMaterialInterface,
|
||||||
erWipeTower,
|
erWipeTower,
|
||||||
|
erCustom,
|
||||||
// Extrusion role for a collection with multiple extrusion roles.
|
// Extrusion role for a collection with multiple extrusion roles.
|
||||||
erMixed,
|
erMixed,
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "FillConcentric.hpp"
|
#include "FillConcentric.hpp"
|
||||||
#include "FillHoneycomb.hpp"
|
#include "FillHoneycomb.hpp"
|
||||||
#include "Fill3DHoneycomb.hpp"
|
#include "Fill3DHoneycomb.hpp"
|
||||||
|
#include "FillGyroid.hpp"
|
||||||
#include "FillPlanePath.hpp"
|
#include "FillPlanePath.hpp"
|
||||||
#include "FillRectilinear.hpp"
|
#include "FillRectilinear.hpp"
|
||||||
#include "FillRectilinear2.hpp"
|
#include "FillRectilinear2.hpp"
|
||||||
|
@ -21,6 +22,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
|
||||||
case ipConcentric: return new FillConcentric();
|
case ipConcentric: return new FillConcentric();
|
||||||
case ipHoneycomb: return new FillHoneycomb();
|
case ipHoneycomb: return new FillHoneycomb();
|
||||||
case ip3DHoneycomb: return new Fill3DHoneycomb();
|
case ip3DHoneycomb: return new Fill3DHoneycomb();
|
||||||
|
case ipGyroid: return new FillGyroid();
|
||||||
case ipRectilinear: return new FillRectilinear2();
|
case ipRectilinear: return new FillRectilinear2();
|
||||||
// case ipRectilinear: return new FillRectilinear();
|
// case ipRectilinear: return new FillRectilinear();
|
||||||
case ipLine: return new FillLine();
|
case ipLine: return new FillLine();
|
||||||
|
|
150
xs/src/libslic3r/Fill/FillGyroid.cpp
Normal file
150
xs/src/libslic3r/Fill/FillGyroid.cpp
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
#include "../ClipperUtils.hpp"
|
||||||
|
#include "../PolylineCollection.hpp"
|
||||||
|
#include "../Surface.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "FillGyroid.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
static inline Polyline make_wave_vertical(
|
||||||
|
double width, double height, double x0,
|
||||||
|
double segmentSize, double scaleFactor,
|
||||||
|
double z_cos, double z_sin, bool flip)
|
||||||
|
{
|
||||||
|
Polyline polyline;
|
||||||
|
polyline.points.emplace_back(Point(coord_t(clamp(0., width, x0) * scaleFactor), 0));
|
||||||
|
double phase_offset_sin = (z_cos < 0 ? M_PI : 0) + M_PI;
|
||||||
|
double phase_offset_cos = (z_cos < 0 ? M_PI : 0) + M_PI + (flip ? M_PI : 0.);
|
||||||
|
for (double y = 0.; y < height + segmentSize; y += segmentSize) {
|
||||||
|
y = std::min(y, height);
|
||||||
|
double a = sin(y + phase_offset_sin);
|
||||||
|
double b = - z_cos;
|
||||||
|
double res = z_sin * cos(y + phase_offset_cos);
|
||||||
|
double r = sqrt(sqr(a) + sqr(b));
|
||||||
|
double x = clamp(0., width, asin(a/r) + asin(res/r) + M_PI + x0);
|
||||||
|
polyline.points.emplace_back(convert_to<Point>(Pointf(x, y) * scaleFactor));
|
||||||
|
}
|
||||||
|
if (flip)
|
||||||
|
std::reverse(polyline.points.begin(), polyline.points.end());
|
||||||
|
return polyline;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Polyline make_wave_horizontal(
|
||||||
|
double width, double height, double y0,
|
||||||
|
double segmentSize, double scaleFactor,
|
||||||
|
double z_cos, double z_sin, bool flip)
|
||||||
|
{
|
||||||
|
Polyline polyline;
|
||||||
|
polyline.points.emplace_back(Point(0, coord_t(clamp(0., height, y0) * scaleFactor)));
|
||||||
|
double phase_offset_sin = (z_sin < 0 ? M_PI : 0) + (flip ? 0 : M_PI);
|
||||||
|
double phase_offset_cos = z_sin < 0 ? M_PI : 0.;
|
||||||
|
for (double x=0.; x < width + segmentSize; x += segmentSize) {
|
||||||
|
x = std::min(x, width);
|
||||||
|
double a = cos(x + phase_offset_cos);
|
||||||
|
double b = - z_sin;
|
||||||
|
double res = z_cos * sin(x + phase_offset_sin);
|
||||||
|
double r = sqrt(sqr(a) + sqr(b));
|
||||||
|
double y = clamp(0., height, asin(a/r) + asin(res/r) + 0.5 * M_PI + y0);
|
||||||
|
polyline.points.emplace_back(convert_to<Point>(Pointf(x, y) * scaleFactor));
|
||||||
|
}
|
||||||
|
if (flip)
|
||||||
|
std::reverse(polyline.points.begin(), polyline.points.end());
|
||||||
|
return polyline;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Polylines make_gyroid_waves(double gridZ, double density, double layer_width, double width, double height)
|
||||||
|
{
|
||||||
|
double scaleFactor = scale_(layer_width) / density;
|
||||||
|
double segmentSize = 0.5 * density;
|
||||||
|
//scale factor for 5% : 8 712 388
|
||||||
|
// 1z = 10^-6 mm ?
|
||||||
|
double z = gridZ / scaleFactor;
|
||||||
|
double z_sin = sin(z);
|
||||||
|
double z_cos = cos(z);
|
||||||
|
Polylines result;
|
||||||
|
if (abs(z_sin) <= abs(z_cos)) {
|
||||||
|
// Vertical wave
|
||||||
|
double x0 = M_PI * (int)((- 0.5 * M_PI) / M_PI - 1.);
|
||||||
|
bool flip = ((int)(x0 / M_PI + 1.) & 1) != 0;
|
||||||
|
for (; x0 < width - 0.5 * M_PI; x0 += M_PI, flip = ! flip)
|
||||||
|
result.emplace_back(make_wave_vertical(width, height, x0, segmentSize, scaleFactor, z_cos, z_sin, flip));
|
||||||
|
} else {
|
||||||
|
// Horizontal wave
|
||||||
|
bool flip = true;
|
||||||
|
for (double y0 = 0.; y0 < width; y0 += M_PI, flip = !flip)
|
||||||
|
result.emplace_back(make_wave_horizontal(width, height, y0, segmentSize, scaleFactor, z_cos, z_sin, flip));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillGyroid::_fill_surface_single(
|
||||||
|
const FillParams ¶ms,
|
||||||
|
unsigned int thickness_layers,
|
||||||
|
const std::pair<float, Point> &direction,
|
||||||
|
ExPolygon &expolygon,
|
||||||
|
Polylines &polylines_out)
|
||||||
|
{
|
||||||
|
// no rotation is supported for this infill pattern
|
||||||
|
BoundingBox bb = expolygon.contour.bounding_box();
|
||||||
|
coord_t distance = coord_t(scale_(this->spacing) / (params.density*this->scaling));
|
||||||
|
|
||||||
|
// align bounding box to a multiple of our grid module
|
||||||
|
bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
|
||||||
|
|
||||||
|
// generate pattern
|
||||||
|
Polylines polylines = make_gyroid_waves(
|
||||||
|
scale_(this->z),
|
||||||
|
params.density*this->scaling,
|
||||||
|
this->spacing,
|
||||||
|
ceil(bb.size().x / distance) + 1.,
|
||||||
|
ceil(bb.size().y / distance) + 1.);
|
||||||
|
|
||||||
|
// move pattern in place
|
||||||
|
for (Polyline &polyline : polylines)
|
||||||
|
polyline.translate(bb.min.x, bb.min.y);
|
||||||
|
|
||||||
|
// clip pattern to boundaries
|
||||||
|
polylines = intersection_pl(polylines, (Polygons)expolygon);
|
||||||
|
|
||||||
|
// connect lines
|
||||||
|
if (! params.dont_connect && ! polylines.empty()) { // prevent calling leftmost_point() on empty collections
|
||||||
|
ExPolygon expolygon_off;
|
||||||
|
{
|
||||||
|
ExPolygons expolygons_off = offset_ex(expolygon, (float)SCALED_EPSILON);
|
||||||
|
if (! expolygons_off.empty()) {
|
||||||
|
// When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island.
|
||||||
|
assert(expolygons_off.size() == 1);
|
||||||
|
std::swap(expolygon_off, expolygons_off.front());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Polylines chained = PolylineCollection::chained_path_from(
|
||||||
|
std::move(polylines),
|
||||||
|
PolylineCollection::leftmost_point(polylines), false); // reverse allowed
|
||||||
|
bool first = true;
|
||||||
|
for (Polyline &polyline : chained) {
|
||||||
|
if (! first) {
|
||||||
|
// Try to connect the lines.
|
||||||
|
Points &pts_end = polylines_out.back().points;
|
||||||
|
const Point &first_point = polyline.points.front();
|
||||||
|
const Point &last_point = pts_end.back();
|
||||||
|
// TODO: we should also check that both points are on a fill_boundary to avoid
|
||||||
|
// connecting paths on the boundaries of internal regions
|
||||||
|
// TODO: avoid crossing current infill path
|
||||||
|
if (first_point.distance_to(last_point) <= 5 * distance &&
|
||||||
|
expolygon_off.contains(Line(last_point, first_point))) {
|
||||||
|
// Append the polyline.
|
||||||
|
pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The lines cannot be connected.
|
||||||
|
polylines_out.emplace_back(std::move(polyline));
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
34
xs/src/libslic3r/Fill/FillGyroid.hpp
Normal file
34
xs/src/libslic3r/Fill/FillGyroid.hpp
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef slic3r_FillGyroid_hpp_
|
||||||
|
#define slic3r_FillGyroid_hpp_
|
||||||
|
|
||||||
|
#include "../libslic3r.h"
|
||||||
|
|
||||||
|
#include "FillBase.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class FillGyroid : public Fill
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FillGyroid() {}
|
||||||
|
virtual Fill* clone() const { return new FillGyroid(*this); }
|
||||||
|
|
||||||
|
// require bridge flow since most of this pattern hangs in air
|
||||||
|
virtual bool use_bridge_flow() const { return true; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// mult of density, to have a good %of weight for each density parameter
|
||||||
|
float scaling = 1.75;
|
||||||
|
|
||||||
|
virtual void _fill_surface_single(
|
||||||
|
const FillParams ¶ms,
|
||||||
|
unsigned int thickness_layers,
|
||||||
|
const std::pair<float, Point> &direction,
|
||||||
|
ExPolygon &expolygon,
|
||||||
|
Polylines &polylines_out);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // slic3r_FillGyroid_hpp_
|
|
@ -587,6 +587,15 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||||
this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
|
this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
|
||||||
// Set extruder(s) temperature before and after start G-code.
|
// Set extruder(s) temperature before and after start G-code.
|
||||||
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false);
|
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false);
|
||||||
|
|
||||||
|
if (m_enable_analyzer)
|
||||||
|
{
|
||||||
|
// adds tag for analyzer
|
||||||
|
char buf[32];
|
||||||
|
sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom);
|
||||||
|
_writeln(file, buf);
|
||||||
|
}
|
||||||
|
|
||||||
// Write the custom start G-code
|
// Write the custom start G-code
|
||||||
_writeln(file, start_gcode);
|
_writeln(file, start_gcode);
|
||||||
// Process filament-specific gcode in extruder order.
|
// Process filament-specific gcode in extruder order.
|
||||||
|
@ -770,6 +779,15 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
||||||
// Write end commands to file.
|
// Write end commands to file.
|
||||||
_write(file, this->retract());
|
_write(file, this->retract());
|
||||||
_write(file, m_writer.set_fan(false));
|
_write(file, m_writer.set_fan(false));
|
||||||
|
|
||||||
|
if (m_enable_analyzer)
|
||||||
|
{
|
||||||
|
// adds tag for analyzer
|
||||||
|
char buf[32];
|
||||||
|
sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erCustom);
|
||||||
|
_writeln(file, buf);
|
||||||
|
}
|
||||||
|
|
||||||
// Process filament-specific gcode in extruder order.
|
// Process filament-specific gcode in extruder order.
|
||||||
if (print.config.single_extruder_multi_material) {
|
if (print.config.single_extruder_multi_material) {
|
||||||
// Process the end_filament_gcode for the active filament only.
|
// Process the end_filament_gcode for the active filament only.
|
||||||
|
@ -1384,18 +1402,13 @@ void GCode::apply_print_config(const PrintConfig &print_config)
|
||||||
|
|
||||||
void GCode::append_full_config(const Print& print, std::string& str)
|
void GCode::append_full_config(const Print& print, std::string& str)
|
||||||
{
|
{
|
||||||
char buff[4096];
|
|
||||||
|
|
||||||
const StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config };
|
const StaticPrintConfig *configs[] = { &print.config, &print.default_object_config, &print.default_region_config };
|
||||||
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) {
|
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) {
|
||||||
const StaticPrintConfig *cfg = configs[i];
|
const StaticPrintConfig *cfg = configs[i];
|
||||||
for (const std::string &key : cfg->keys())
|
for (const std::string &key : cfg->keys())
|
||||||
{
|
{
|
||||||
if (key != "compatible_printers")
|
if (key != "compatible_printers")
|
||||||
{
|
str += "; " + key + " = " + cfg->serialize(key) + "\n";
|
||||||
sprintf(buff, "; %s = %s\n", key.c_str(), cfg->serialize(key).c_str());
|
|
||||||
str += buff;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,13 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi
|
||||||
{
|
{
|
||||||
// processes 'special' comments contained in line
|
// processes 'special' comments contained in line
|
||||||
if (_process_tags(line))
|
if (_process_tags(line))
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
// DEBUG ONLY: puts the line back into the gcode
|
||||||
|
m_process_output += line.raw() + "\n";
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// sets new start position/extrusion
|
// sets new start position/extrusion
|
||||||
_set_start_position(_get_end_position());
|
_set_start_position(_get_end_position());
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "Analyzer.hpp"
|
#include "Analyzer.hpp"
|
||||||
#include "PreviewData.hpp"
|
#include "PreviewData.hpp"
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
#include <wx/intl.h>
|
||||||
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -125,26 +127,28 @@ const GCodePreviewData::Color GCodePreviewData::Extrusion::Default_Extrusion_Rol
|
||||||
Color(0.0f, 0.5f, 0.0f, 1.0f), // erSupportMaterial
|
Color(0.0f, 0.5f, 0.0f, 1.0f), // erSupportMaterial
|
||||||
Color(0.0f, 0.0f, 0.5f, 1.0f), // erSupportMaterialInterface
|
Color(0.0f, 0.0f, 0.5f, 1.0f), // erSupportMaterialInterface
|
||||||
Color(0.7f, 0.89f, 0.67f, 1.0f), // erWipeTower
|
Color(0.7f, 0.89f, 0.67f, 1.0f), // erWipeTower
|
||||||
|
Color(1.0f, 1.0f, 0.0f, 1.0f), // erCustom
|
||||||
Color(0.0f, 0.0f, 0.0f, 1.0f) // erMixed
|
Color(0.0f, 0.0f, 0.0f, 1.0f) // erMixed
|
||||||
};
|
};
|
||||||
|
|
||||||
// todo: merge with Slic3r::ExtrusionRole2String() from GCode.cpp
|
// todo: merge with Slic3r::ExtrusionRole2String() from GCode.cpp
|
||||||
const std::string GCodePreviewData::Extrusion::Default_Extrusion_Role_Names[Num_Extrusion_Roles]
|
const std::string GCodePreviewData::Extrusion::Default_Extrusion_Role_Names[Num_Extrusion_Roles]
|
||||||
{
|
{
|
||||||
"None",
|
L("None"),
|
||||||
"Perimeter",
|
L("Perimeter"),
|
||||||
"External perimeter",
|
L("External perimeter"),
|
||||||
"Overhang perimeter",
|
L("Overhang perimeter"),
|
||||||
"Internal infill",
|
L("Internal infill"),
|
||||||
"Solid infill",
|
L("Solid infill"),
|
||||||
"Top solid infill",
|
L("Top solid infill"),
|
||||||
"Bridge infill",
|
L("Bridge infill"),
|
||||||
"Gap fill",
|
L("Gap fill"),
|
||||||
"Skirt",
|
L("Skirt"),
|
||||||
"Support material",
|
L("Support material"),
|
||||||
"Support material interface",
|
L("Support material interface"),
|
||||||
"Wipe tower",
|
L("Wipe tower"),
|
||||||
"Mixed"
|
L("Custom"),
|
||||||
|
L("Mixed")
|
||||||
};
|
};
|
||||||
|
|
||||||
const GCodePreviewData::Extrusion::EViewType GCodePreviewData::Extrusion::Default_View_Type = GCodePreviewData::Extrusion::FeatureType;
|
const GCodePreviewData::Extrusion::EViewType GCodePreviewData::Extrusion::Default_View_Type = GCodePreviewData::Extrusion::FeatureType;
|
||||||
|
@ -323,15 +327,15 @@ std::string GCodePreviewData::get_legend_title() const
|
||||||
switch (extrusion.view_type)
|
switch (extrusion.view_type)
|
||||||
{
|
{
|
||||||
case Extrusion::FeatureType:
|
case Extrusion::FeatureType:
|
||||||
return "Feature type";
|
return L("Feature type");
|
||||||
case Extrusion::Height:
|
case Extrusion::Height:
|
||||||
return "Height (mm)";
|
return L("Height (mm)");
|
||||||
case Extrusion::Width:
|
case Extrusion::Width:
|
||||||
return "Width (mm)";
|
return L("Width (mm)");
|
||||||
case Extrusion::Feedrate:
|
case Extrusion::Feedrate:
|
||||||
return "Speed (mm/s)";
|
return L("Speed (mm/s)");
|
||||||
case Extrusion::Tool:
|
case Extrusion::Tool:
|
||||||
return "Tool";
|
return L("Tool");
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
@ -360,10 +364,13 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
|
||||||
{
|
{
|
||||||
case Extrusion::FeatureType:
|
case Extrusion::FeatureType:
|
||||||
{
|
{
|
||||||
items.reserve(erMixed - erPerimeter + 1);
|
ExtrusionRole first_valid = erPerimeter;
|
||||||
for (unsigned int i = (unsigned int)erPerimeter; i < (unsigned int)erMixed; ++i)
|
ExtrusionRole last_valid = erCustom;
|
||||||
|
|
||||||
|
items.reserve(last_valid - first_valid + 1);
|
||||||
|
for (unsigned int i = (unsigned int)first_valid; i <= (unsigned int)last_valid; ++i)
|
||||||
{
|
{
|
||||||
items.emplace_back(extrusion.role_names[i], extrusion.role_colors[i]);
|
items.emplace_back(_CHB(extrusion.role_names[i].c_str()).data(), extrusion.role_colors[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -389,8 +396,8 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
|
||||||
items.reserve(tools_colors_count);
|
items.reserve(tools_colors_count);
|
||||||
for (unsigned int i = 0; i < tools_colors_count; ++i)
|
for (unsigned int i = 0; i < tools_colors_count; ++i)
|
||||||
{
|
{
|
||||||
char buf[32];
|
char buf[MIN_BUF_LENGTH_FOR_L];
|
||||||
sprintf(buf, "Extruder %d", i + 1);
|
sprintf(buf, _CHB(L("Extruder %d")), i + 1);
|
||||||
|
|
||||||
GCodePreviewData::Color color;
|
GCodePreviewData::Color color;
|
||||||
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
|
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
|
||||||
|
|
|
@ -19,6 +19,18 @@ static const float DEFAULT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE = 1.0f; // 100 per
|
||||||
|
|
||||||
static const float PREVIOUS_FEEDRATE_THRESHOLD = 0.0001f;
|
static const float PREVIOUS_FEEDRATE_THRESHOLD = 0.0001f;
|
||||||
|
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
static const std::string MOVE_TYPE_STR[Slic3r::GCodeTimeEstimator::Block::Num_Types] =
|
||||||
|
{
|
||||||
|
"Noop",
|
||||||
|
"Retract",
|
||||||
|
"Unretract",
|
||||||
|
"Tool_change",
|
||||||
|
"Move",
|
||||||
|
"Extrude"
|
||||||
|
};
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
void GCodeTimeEstimator::Feedrates::reset()
|
void GCodeTimeEstimator::Feedrates::reset()
|
||||||
|
@ -139,6 +151,14 @@ namespace Slic3r {
|
||||||
return (acceleration == 0.0f) ? 0.0f : (2.0f * acceleration * distance - sqr(initial_rate) + sqr(final_rate)) / (4.0f * acceleration);
|
return (acceleration == 0.0f) ? 0.0f : (2.0f * acceleration * distance - sqr(initial_rate) + sqr(final_rate)) / (4.0f * acceleration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
GCodeTimeEstimator::MoveStats::MoveStats()
|
||||||
|
: count(0)
|
||||||
|
, time(0.0f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
|
|
||||||
GCodeTimeEstimator::GCodeTimeEstimator()
|
GCodeTimeEstimator::GCodeTimeEstimator()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
@ -155,6 +175,10 @@ namespace Slic3r {
|
||||||
|
|
||||||
_calculate_time();
|
_calculate_time();
|
||||||
|
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
_log_moves_stats();
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
|
|
||||||
_reset_blocks();
|
_reset_blocks();
|
||||||
_reset();
|
_reset();
|
||||||
}
|
}
|
||||||
|
@ -166,6 +190,10 @@ namespace Slic3r {
|
||||||
_parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2));
|
_parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2));
|
||||||
_calculate_time();
|
_calculate_time();
|
||||||
|
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
_log_moves_stats();
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
|
|
||||||
_reset_blocks();
|
_reset_blocks();
|
||||||
_reset();
|
_reset();
|
||||||
}
|
}
|
||||||
|
@ -180,6 +208,10 @@ namespace Slic3r {
|
||||||
_parser.parse_line(line, action);
|
_parser.parse_line(line, action);
|
||||||
_calculate_time();
|
_calculate_time();
|
||||||
|
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
_log_moves_stats();
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
|
|
||||||
_reset_blocks();
|
_reset_blocks();
|
||||||
_reset();
|
_reset();
|
||||||
}
|
}
|
||||||
|
@ -208,6 +240,11 @@ namespace Slic3r {
|
||||||
{
|
{
|
||||||
PROFILE_FUNC();
|
PROFILE_FUNC();
|
||||||
_calculate_time();
|
_calculate_time();
|
||||||
|
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
_log_moves_stats();
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
|
|
||||||
_reset_blocks();
|
_reset_blocks();
|
||||||
_reset();
|
_reset();
|
||||||
}
|
}
|
||||||
|
@ -393,6 +430,9 @@ namespace Slic3r {
|
||||||
void GCodeTimeEstimator::reset()
|
void GCodeTimeEstimator::reset()
|
||||||
{
|
{
|
||||||
_time = 0.0f;
|
_time = 0.0f;
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
_moves_stats.clear();
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
_reset_blocks();
|
_reset_blocks();
|
||||||
_reset();
|
_reset();
|
||||||
}
|
}
|
||||||
|
@ -448,9 +488,24 @@ namespace Slic3r {
|
||||||
|
|
||||||
for (const Block& block : _blocks)
|
for (const Block& block : _blocks)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
float block_time = 0.0f;
|
||||||
|
block_time += block.acceleration_time();
|
||||||
|
block_time += block.cruise_time();
|
||||||
|
block_time += block.deceleration_time();
|
||||||
|
_time += block_time;
|
||||||
|
|
||||||
|
MovesStatsMap::iterator it = _moves_stats.find(block.move_type);
|
||||||
|
if (it == _moves_stats.end())
|
||||||
|
it = _moves_stats.insert(MovesStatsMap::value_type(block.move_type, MoveStats())).first;
|
||||||
|
|
||||||
|
it->second.count += 1;
|
||||||
|
it->second.time += block_time;
|
||||||
|
#else
|
||||||
_time += block.acceleration_time();
|
_time += block.acceleration_time();
|
||||||
_time += block.cruise_time();
|
_time += block.cruise_time();
|
||||||
_time += block.deceleration_time();
|
_time += block.deceleration_time();
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,6 +801,28 @@ namespace Slic3r {
|
||||||
set_axis_position((EAxis)a, new_pos[a]);
|
set_axis_position((EAxis)a, new_pos[a]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
// detects block move type
|
||||||
|
block.move_type = Block::Noop;
|
||||||
|
|
||||||
|
if (block.delta_pos[E] < 0.0f)
|
||||||
|
{
|
||||||
|
if ((block.delta_pos[X] != 0.0f) || (block.delta_pos[Y] != 0.0f) || (block.delta_pos[Z] != 0.0f))
|
||||||
|
block.move_type = Block::Move;
|
||||||
|
else
|
||||||
|
block.move_type = Block::Retract;
|
||||||
|
}
|
||||||
|
else if (block.delta_pos[E] > 0.0f)
|
||||||
|
{
|
||||||
|
if ((block.delta_pos[X] == 0.0f) && (block.delta_pos[Y] == 0.0f) && (block.delta_pos[Z] == 0.0f))
|
||||||
|
block.move_type = Block::Unretract;
|
||||||
|
else if ((block.delta_pos[X] != 0.0f) || (block.delta_pos[Y] != 0.0f))
|
||||||
|
block.move_type = Block::Extrude;
|
||||||
|
}
|
||||||
|
else if ((block.delta_pos[X] != 0.0f) || (block.delta_pos[Y] != 0.0f) || (block.delta_pos[Z] != 0.0f))
|
||||||
|
block.move_type = Block::Move;
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
|
|
||||||
// adds block to blocks list
|
// adds block to blocks list
|
||||||
_blocks.emplace_back(block);
|
_blocks.emplace_back(block);
|
||||||
}
|
}
|
||||||
|
@ -1064,4 +1141,24 @@ namespace Slic3r {
|
||||||
next->flags.recalculate = false;
|
next->flags.recalculate = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
void GCodeTimeEstimator::_log_moves_stats() const
|
||||||
|
{
|
||||||
|
float moves_count = 0.0f;
|
||||||
|
for (const MovesStatsMap::value_type& move : _moves_stats)
|
||||||
|
{
|
||||||
|
moves_count += (float)move.second.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const MovesStatsMap::value_type& move : _moves_stats)
|
||||||
|
{
|
||||||
|
std::cout << MOVE_TYPE_STR[move.first];
|
||||||
|
std::cout << ": count " << move.second.count << " (" << 100.0f * (float)move.second.count / moves_count << "%)";
|
||||||
|
std::cout << " - time: " << move.second.time << "s (" << 100.0f * move.second.time / _time << "%)";
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include "PrintConfig.hpp"
|
#include "PrintConfig.hpp"
|
||||||
#include "GCodeReader.hpp"
|
#include "GCodeReader.hpp"
|
||||||
|
|
||||||
|
#define ENABLE_MOVE_STATS 0
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -74,6 +76,19 @@ namespace Slic3r {
|
||||||
public:
|
public:
|
||||||
struct Block
|
struct Block
|
||||||
{
|
{
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
enum EMoveType : unsigned char
|
||||||
|
{
|
||||||
|
Noop,
|
||||||
|
Retract,
|
||||||
|
Unretract,
|
||||||
|
Tool_change,
|
||||||
|
Move,
|
||||||
|
Extrude,
|
||||||
|
Num_Types
|
||||||
|
};
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
|
|
||||||
struct FeedrateProfile
|
struct FeedrateProfile
|
||||||
{
|
{
|
||||||
float entry; // mm/s
|
float entry; // mm/s
|
||||||
|
@ -106,6 +121,10 @@ namespace Slic3r {
|
||||||
bool nominal_length;
|
bool nominal_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
EMoveType move_type;
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
Flags flags;
|
Flags flags;
|
||||||
|
|
||||||
float delta_pos[Num_Axis]; // mm
|
float delta_pos[Num_Axis]; // mm
|
||||||
|
@ -156,6 +175,18 @@ namespace Slic3r {
|
||||||
|
|
||||||
typedef std::vector<Block> BlocksList;
|
typedef std::vector<Block> BlocksList;
|
||||||
|
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
struct MoveStats
|
||||||
|
{
|
||||||
|
unsigned int count;
|
||||||
|
float time;
|
||||||
|
|
||||||
|
MoveStats();
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::map<Block::EMoveType, MoveStats> MovesStatsMap;
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GCodeReader _parser;
|
GCodeReader _parser;
|
||||||
State _state;
|
State _state;
|
||||||
|
@ -163,6 +194,9 @@ namespace Slic3r {
|
||||||
Feedrates _prev;
|
Feedrates _prev;
|
||||||
BlocksList _blocks;
|
BlocksList _blocks;
|
||||||
float _time; // s
|
float _time; // s
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
MovesStatsMap _moves_stats;
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GCodeTimeEstimator();
|
GCodeTimeEstimator();
|
||||||
|
@ -318,6 +352,10 @@ namespace Slic3r {
|
||||||
void _planner_reverse_pass_kernel(Block& curr, Block& next);
|
void _planner_reverse_pass_kernel(Block& curr, Block& next);
|
||||||
|
|
||||||
void _recalculate_trapezoids();
|
void _recalculate_trapezoids();
|
||||||
|
|
||||||
|
#if ENABLE_MOVE_STATS
|
||||||
|
void _log_moves_stats() const;
|
||||||
|
#endif // ENABLE_MOVE_STATS
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace Slic3r */
|
} /* namespace Slic3r */
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/nowide/iostream.hpp>
|
#include <boost/nowide/iostream.hpp>
|
||||||
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -91,7 +92,15 @@ Model Model::read_from_archive(const std::string &input_file, PresetBundle* bund
|
||||||
throw std::runtime_error("The supplied file couldn't be read because it's empty");
|
throw std::runtime_error("The supplied file couldn't be read because it's empty");
|
||||||
|
|
||||||
for (ModelObject *o : model.objects)
|
for (ModelObject *o : model.objects)
|
||||||
o->input_file = input_file;
|
{
|
||||||
|
if (boost::algorithm::iends_with(input_file, ".zip.amf"))
|
||||||
|
{
|
||||||
|
// we remove the .zip part of the extension to avoid it be added to filenames when exporting
|
||||||
|
o->input_file = boost::ireplace_last_copy(input_file, ".zip.", ".");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
o->input_file = input_file;
|
||||||
|
}
|
||||||
|
|
||||||
if (add_default_instances)
|
if (add_default_instances)
|
||||||
model.add_default_instances();
|
model.add_default_instances();
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -29,7 +29,7 @@ enum GCodeFlavor {
|
||||||
|
|
||||||
enum InfillPattern {
|
enum InfillPattern {
|
||||||
ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||||
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
|
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SupportMaterialPattern {
|
enum SupportMaterialPattern {
|
||||||
|
@ -73,6 +73,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enu
|
||||||
keys_map["concentric"] = ipConcentric;
|
keys_map["concentric"] = ipConcentric;
|
||||||
keys_map["honeycomb"] = ipHoneycomb;
|
keys_map["honeycomb"] = ipHoneycomb;
|
||||||
keys_map["3dhoneycomb"] = ip3DHoneycomb;
|
keys_map["3dhoneycomb"] = ip3DHoneycomb;
|
||||||
|
keys_map["gyroid"] = ipGyroid;
|
||||||
keys_map["hilbertcurve"] = ipHilbertCurve;
|
keys_map["hilbertcurve"] = ipHilbertCurve;
|
||||||
keys_map["archimedeanchords"] = ipArchimedeanChords;
|
keys_map["archimedeanchords"] = ipArchimedeanChords;
|
||||||
keys_map["octagramspiral"] = ipOctagramSpiral;
|
keys_map["octagramspiral"] = ipOctagramSpiral;
|
||||||
|
@ -699,6 +700,7 @@ class HostConfig : public StaticPrintConfig
|
||||||
public:
|
public:
|
||||||
ConfigOptionString octoprint_host;
|
ConfigOptionString octoprint_host;
|
||||||
ConfigOptionString octoprint_apikey;
|
ConfigOptionString octoprint_apikey;
|
||||||
|
ConfigOptionString octoprint_cafile;
|
||||||
ConfigOptionString serial_port;
|
ConfigOptionString serial_port;
|
||||||
ConfigOptionInt serial_speed;
|
ConfigOptionInt serial_speed;
|
||||||
|
|
||||||
|
@ -707,6 +709,7 @@ protected:
|
||||||
{
|
{
|
||||||
OPT_PTR(octoprint_host);
|
OPT_PTR(octoprint_host);
|
||||||
OPT_PTR(octoprint_apikey);
|
OPT_PTR(octoprint_apikey);
|
||||||
|
OPT_PTR(octoprint_cafile);
|
||||||
OPT_PTR(serial_port);
|
OPT_PTR(serial_port);
|
||||||
OPT_PTR(serial_speed);
|
OPT_PTR(serial_speed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,9 +198,8 @@ TriangleMesh::repair() {
|
||||||
stl_clear_error(&stl);
|
stl_clear_error(&stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// commenting out the following call fixes: #574, #413, #269, #262, #259, #230, #228, #206
|
// normal_directions
|
||||||
// // normal_directions
|
stl_fix_normal_directions(&stl);
|
||||||
// stl_fix_normal_directions(&stl);
|
|
||||||
|
|
||||||
// normal_values
|
// normal_values
|
||||||
stl_fix_normal_values(&stl);
|
stl_fix_normal_values(&stl);
|
||||||
|
@ -210,7 +209,7 @@ TriangleMesh::repair() {
|
||||||
|
|
||||||
// neighbors
|
// neighbors
|
||||||
stl_verify_neighbors(&stl);
|
stl_verify_neighbors(&stl);
|
||||||
|
|
||||||
this->repaired = true;
|
this->repaired = true;
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
|
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
|
||||||
|
@ -1187,40 +1186,46 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
|
||||||
loops correctly in some edge cases when original model had overlapping facets
|
loops correctly in some edge cases when original model had overlapping facets
|
||||||
*/
|
*/
|
||||||
|
|
||||||
std::vector<double> area;
|
/* The following lines are commented out because they can generate wrong polygons,
|
||||||
std::vector<size_t> sorted_area; // vector of indices
|
see for example issue #661 */
|
||||||
for (Polygons::const_iterator loop = loops.begin(); loop != loops.end(); ++ loop) {
|
|
||||||
area.push_back(loop->area());
|
|
||||||
sorted_area.push_back(loop - loops.begin());
|
|
||||||
}
|
|
||||||
|
|
||||||
// outer first
|
|
||||||
std::sort(sorted_area.begin(), sorted_area.end(),
|
|
||||||
[&area](size_t a, size_t b) { return std::abs(area[a]) > std::abs(area[b]); });
|
|
||||||
|
|
||||||
// we don't perform a safety offset now because it might reverse cw loops
|
//std::vector<double> area;
|
||||||
Polygons p_slices;
|
//std::vector<size_t> sorted_area; // vector of indices
|
||||||
for (std::vector<size_t>::const_iterator loop_idx = sorted_area.begin(); loop_idx != sorted_area.end(); ++ loop_idx) {
|
//for (Polygons::const_iterator loop = loops.begin(); loop != loops.end(); ++ loop) {
|
||||||
/* we rely on the already computed area to determine the winding order
|
// area.push_back(loop->area());
|
||||||
of the loops, since the Orientation() function provided by Clipper
|
// sorted_area.push_back(loop - loops.begin());
|
||||||
would do the same, thus repeating the calculation */
|
//}
|
||||||
Polygons::const_iterator loop = loops.begin() + *loop_idx;
|
//
|
||||||
if (area[*loop_idx] > +EPSILON)
|
//// outer first
|
||||||
p_slices.push_back(*loop);
|
//std::sort(sorted_area.begin(), sorted_area.end(),
|
||||||
else if (area[*loop_idx] < -EPSILON)
|
// [&area](size_t a, size_t b) { return std::abs(area[a]) > std::abs(area[b]); });
|
||||||
//FIXME This is arbitrary and possibly very slow.
|
|
||||||
// If the hole is inside a polygon, then there is no need to diff.
|
//// we don't perform a safety offset now because it might reverse cw loops
|
||||||
// If the hole intersects a polygon boundary, then diff it, but then
|
//Polygons p_slices;
|
||||||
// there is no guarantee of an ordering of the loops.
|
//for (std::vector<size_t>::const_iterator loop_idx = sorted_area.begin(); loop_idx != sorted_area.end(); ++ loop_idx) {
|
||||||
// Maybe we can test for the intersection before running the expensive diff algorithm?
|
// /* we rely on the already computed area to determine the winding order
|
||||||
p_slices = diff(p_slices, *loop);
|
// of the loops, since the Orientation() function provided by Clipper
|
||||||
}
|
// would do the same, thus repeating the calculation */
|
||||||
|
// Polygons::const_iterator loop = loops.begin() + *loop_idx;
|
||||||
|
// if (area[*loop_idx] > +EPSILON)
|
||||||
|
// p_slices.push_back(*loop);
|
||||||
|
// else if (area[*loop_idx] < -EPSILON)
|
||||||
|
// //FIXME This is arbitrary and possibly very slow.
|
||||||
|
// // If the hole is inside a polygon, then there is no need to diff.
|
||||||
|
// // If the hole intersects a polygon boundary, then diff it, but then
|
||||||
|
// // there is no guarantee of an ordering of the loops.
|
||||||
|
// // Maybe we can test for the intersection before running the expensive diff algorithm?
|
||||||
|
// p_slices = diff(p_slices, *loop);
|
||||||
|
//}
|
||||||
|
|
||||||
// perform a safety offset to merge very close facets (TODO: find test case for this)
|
// perform a safety offset to merge very close facets (TODO: find test case for this)
|
||||||
double safety_offset = scale_(0.0499);
|
double safety_offset = scale_(0.0499);
|
||||||
//FIXME see https://github.com/prusa3d/Slic3r/issues/520
|
//FIXME see https://github.com/prusa3d/Slic3r/issues/520
|
||||||
// double safety_offset = scale_(0.0001);
|
// double safety_offset = scale_(0.0001);
|
||||||
ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset);
|
|
||||||
|
/* The following line is commented out because it can generate wrong polygons,
|
||||||
|
see for example issue #661 */
|
||||||
|
//ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset);
|
||||||
|
|
||||||
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
|
||||||
size_t holes_count = 0;
|
size_t holes_count = 0;
|
||||||
|
@ -1231,7 +1236,10 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// append to the supplied collection
|
// append to the supplied collection
|
||||||
expolygons_append(*slices, ex_slices);
|
/* Fix for issue #661 { */
|
||||||
|
expolygons_append(*slices, offset2_ex(union_(loops, false), +safety_offset, -safety_offset));
|
||||||
|
//expolygons_append(*slices, ex_slices);
|
||||||
|
/* } */
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
|
void TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, ExPolygons* slices) const
|
||||||
|
|
|
@ -64,6 +64,7 @@ REGISTER_CLASS(PresetCollection, "GUI::PresetCollection");
|
||||||
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
|
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
|
||||||
REGISTER_CLASS(PresetHints, "GUI::PresetHints");
|
REGISTER_CLASS(PresetHints, "GUI::PresetHints");
|
||||||
REGISTER_CLASS(TabIface, "GUI::Tab");
|
REGISTER_CLASS(TabIface, "GUI::Tab");
|
||||||
|
REGISTER_CLASS(OctoPrint, "OctoPrint");
|
||||||
|
|
||||||
SV* ConfigBase__as_hash(ConfigBase* THIS)
|
SV* ConfigBase__as_hash(ConfigBase* THIS)
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
#include <wx/image.h>
|
#include <wx/image.h>
|
||||||
#include <wx/settings.h>
|
#include <wx/settings.h>
|
||||||
|
|
||||||
|
#include "GUI.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh)
|
void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh)
|
||||||
|
@ -454,6 +456,25 @@ void GLVolumeCollection::render_legacy() const
|
||||||
glDisableClientState(GL_NORMAL_ARRAY);
|
glDisableClientState(GL_NORMAL_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<double> GLVolumeCollection::get_current_print_zs() const
|
||||||
|
{
|
||||||
|
std::vector<double> print_zs;
|
||||||
|
|
||||||
|
for (GLVolume *vol : this->volumes)
|
||||||
|
{
|
||||||
|
for (coordf_t z : vol->print_zs)
|
||||||
|
{
|
||||||
|
double round_z = (double)round(z * 100000.0f) / 100000.0f;
|
||||||
|
if (std::find(print_zs.begin(), print_zs.end(), round_z) == print_zs.end())
|
||||||
|
print_zs.push_back(round_z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(print_zs.begin(), print_zs.end());
|
||||||
|
|
||||||
|
return print_zs;
|
||||||
|
}
|
||||||
|
|
||||||
// caller is responsible for supplying NO lines with zero length
|
// caller is responsible for supplying NO lines with zero length
|
||||||
static void thick_lines_to_indexed_vertex_array(
|
static void thick_lines_to_indexed_vertex_array(
|
||||||
const Lines &lines,
|
const Lines &lines,
|
||||||
|
@ -1129,7 +1150,7 @@ bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, con
|
||||||
m_data.clear();
|
m_data.clear();
|
||||||
|
|
||||||
// collects items to render
|
// collects items to render
|
||||||
const std::string& title = preview_data.get_legend_title();
|
auto title = GUI::L_str(preview_data.get_legend_title());
|
||||||
const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors);
|
const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors);
|
||||||
|
|
||||||
unsigned int items_count = (unsigned int)items.size();
|
unsigned int items_count = (unsigned int)items.size();
|
||||||
|
@ -1151,7 +1172,7 @@ bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, con
|
||||||
unsigned int max_text_height = 0;
|
unsigned int max_text_height = 0;
|
||||||
for (const GCodePreviewData::LegendItem& item : items)
|
for (const GCodePreviewData::LegendItem& item : items)
|
||||||
{
|
{
|
||||||
memDC.GetTextExtent(item.text, &w, &h);
|
memDC.GetTextExtent(GUI::from_u8(item.text), &w, &h);
|
||||||
max_text_width = std::max(max_text_width, (unsigned int)w);
|
max_text_width = std::max(max_text_width, (unsigned int)w);
|
||||||
max_text_height = std::max(max_text_height, (unsigned int)h);
|
max_text_height = std::max(max_text_height, (unsigned int)h);
|
||||||
}
|
}
|
||||||
|
@ -1228,7 +1249,7 @@ bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, con
|
||||||
memDC.DrawRectangle(wxRect(icon_x_inner, icon_y + 1, px_inner_square, px_inner_square));
|
memDC.DrawRectangle(wxRect(icon_x_inner, icon_y + 1, px_inner_square, px_inner_square));
|
||||||
|
|
||||||
// draw text
|
// draw text
|
||||||
memDC.DrawText(item.text, text_x, icon_y + text_y_offset);
|
memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset);
|
||||||
|
|
||||||
// update y
|
// update y
|
||||||
icon_y += icon_y_step;
|
icon_y += icon_y_step;
|
||||||
|
@ -2209,6 +2230,9 @@ void _3DScene::_update_gcode_volumes_visibility(const GCodePreviewData& preview_
|
||||||
{
|
{
|
||||||
case GCodePreviewVolumeIndex::Extrusion:
|
case GCodePreviewVolumeIndex::Extrusion:
|
||||||
{
|
{
|
||||||
|
if ((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag == erCustom)
|
||||||
|
volume->zoom_to_volumes = false;
|
||||||
|
|
||||||
volume->is_active = preview_data.extrusion.is_role_flag_set((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag);
|
volume->is_active = preview_data.extrusion.is_role_flag_set((ExtrusionRole)s_gcode_preview_volume_index.first_volumes[i].flag);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -372,6 +372,9 @@ public:
|
||||||
|
|
||||||
void set_render_interleaved_only_volumes(const RenderInterleavedOnlyVolumes& render_interleaved_only_volumes) { _render_interleaved_only_volumes = render_interleaved_only_volumes; }
|
void set_render_interleaved_only_volumes(const RenderInterleavedOnlyVolumes& render_interleaved_only_volumes) { _render_interleaved_only_volumes = render_interleaved_only_volumes; }
|
||||||
|
|
||||||
|
// Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection
|
||||||
|
std::vector<double> get_current_print_zs() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GLVolumeCollection(const GLVolumeCollection &other);
|
GLVolumeCollection(const GLVolumeCollection &other);
|
||||||
GLVolumeCollection& operator=(const GLVolumeCollection &);
|
GLVolumeCollection& operator=(const GLVolumeCollection &);
|
||||||
|
|
|
@ -36,43 +36,43 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
|
||||||
{
|
{
|
||||||
// on_change(nullptr);
|
// on_change(nullptr);
|
||||||
|
|
||||||
auto box = new wxStaticBox(this, wxID_ANY, _L("Shape"));
|
auto box = new wxStaticBox(this, wxID_ANY, _(L("Shape")));
|
||||||
auto sbsizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
auto sbsizer = new wxStaticBoxSizer(box, wxVERTICAL);
|
||||||
|
|
||||||
// shape options
|
// shape options
|
||||||
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(300, -1), wxCHB_TOP);
|
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(300, -1), wxCHB_TOP);
|
||||||
sbsizer->Add(m_shape_options_book);
|
sbsizer->Add(m_shape_options_book);
|
||||||
|
|
||||||
auto optgroup = init_shape_options_page(_L("Rectangular"));
|
auto optgroup = init_shape_options_page(_(L("Rectangular")));
|
||||||
ConfigOptionDef def;
|
ConfigOptionDef def;
|
||||||
def.type = coPoints;
|
def.type = coPoints;
|
||||||
def.default_value = new ConfigOptionPoints{ Pointf(200, 200) };
|
def.default_value = new ConfigOptionPoints{ Pointf(200, 200) };
|
||||||
def.label = _LU8("Size");
|
def.label = L("Size");
|
||||||
def.tooltip = _LU8("Size in X and Y of the rectangular plate.");
|
def.tooltip = L("Size in X and Y of the rectangular plate.");
|
||||||
Option option(def, "rect_size");
|
Option option(def, "rect_size");
|
||||||
optgroup->append_single_option_line(option);
|
optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
def.type = coPoints;
|
def.type = coPoints;
|
||||||
def.default_value = new ConfigOptionPoints{ Pointf(0, 0) };
|
def.default_value = new ConfigOptionPoints{ Pointf(0, 0) };
|
||||||
def.label = _LU8("Origin");
|
def.label = L("Origin");
|
||||||
def.tooltip = _LU8("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
|
def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle.");
|
||||||
option = Option(def, "rect_origin");
|
option = Option(def, "rect_origin");
|
||||||
optgroup->append_single_option_line(option);
|
optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
optgroup = init_shape_options_page(_L("Circular"));
|
optgroup = init_shape_options_page(_(L("Circular")));
|
||||||
def.type = coFloat;
|
def.type = coFloat;
|
||||||
def.default_value = new ConfigOptionFloat(200);
|
def.default_value = new ConfigOptionFloat(200);
|
||||||
def.sidetext = _LU8("mm");
|
def.sidetext = L("mm");
|
||||||
def.label = _LU8("Diameter");
|
def.label = L("Diameter");
|
||||||
def.tooltip = _LU8("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
|
def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center.");
|
||||||
option = Option(def, "diameter");
|
option = Option(def, "diameter");
|
||||||
optgroup->append_single_option_line(option);
|
optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
optgroup = init_shape_options_page(_L("Custom"));
|
optgroup = init_shape_options_page(_(L("Custom")));
|
||||||
Line line{ "", "" };
|
Line line{ "", "" };
|
||||||
line.full_width = 1;
|
line.full_width = 1;
|
||||||
line.widget = [this](wxWindow* parent) {
|
line.widget = [this](wxWindow* parent) {
|
||||||
auto btn = new wxButton(parent, wxID_ANY, _L("Load shape from STL..."), wxDefaultPosition, wxDefaultSize);
|
auto btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL...")), wxDefaultPosition, wxDefaultSize);
|
||||||
|
|
||||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
sizer->Add(btn);
|
sizer->Add(btn);
|
||||||
|
@ -117,7 +117,7 @@ ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title){
|
||||||
|
|
||||||
auto panel = new wxPanel(m_shape_options_book);
|
auto panel = new wxPanel(m_shape_options_book);
|
||||||
ConfigOptionsGroupShp optgroup;
|
ConfigOptionsGroupShp optgroup;
|
||||||
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _L("Settings"));
|
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings")));
|
||||||
|
|
||||||
optgroup->label_width = 100;
|
optgroup->label_width = 100;
|
||||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
||||||
|
@ -290,12 +290,12 @@ void BedShapePanel::update_shape()
|
||||||
void BedShapePanel::load_stl()
|
void BedShapePanel::load_stl()
|
||||||
{
|
{
|
||||||
t_file_wild_card vec_FILE_WILDCARDS = get_file_wild_card();
|
t_file_wild_card vec_FILE_WILDCARDS = get_file_wild_card();
|
||||||
std::vector<std::string> file_types = { "known", "stl", "obj", "amf", "prusa"};
|
std::vector<std::string> file_types = { "known", "stl", "obj", "amf", "3mf", "prusa" };
|
||||||
wxString MODEL_WILDCARD;
|
wxString MODEL_WILDCARD;
|
||||||
for (auto file_type: file_types)
|
for (auto file_type: file_types)
|
||||||
MODEL_WILDCARD += vec_FILE_WILDCARDS.at(file_type) + "|";
|
MODEL_WILDCARD += vec_FILE_WILDCARDS.at(file_type) + "|";
|
||||||
|
|
||||||
auto dialog = new wxFileDialog(this, _L("Choose a file to import bed shape from (STL/OBJ/AMF/PRUSA):"), "", "",
|
auto dialog = new wxFileDialog(this, _(L("Choose a file to import bed shape from (STL/OBJ/AMF/3MF/PRUSA):")), "", "",
|
||||||
MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
if (dialog->ShowModal() != wxID_OK) {
|
if (dialog->ShowModal() != wxID_OK) {
|
||||||
dialog->Destroy();
|
dialog->Destroy();
|
||||||
|
@ -312,7 +312,7 @@ void BedShapePanel::load_stl()
|
||||||
model = Model::read_from_file(file_name);
|
model = Model::read_from_file(file_name);
|
||||||
}
|
}
|
||||||
catch (std::exception &e) {
|
catch (std::exception &e) {
|
||||||
auto msg = _L("Error! ") + file_name + " : " + e.what() + ".";
|
auto msg = _(L("Error! ")) + file_name + " : " + e.what() + ".";
|
||||||
show_error(this, msg);
|
show_error(this, msg);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -321,11 +321,11 @@ void BedShapePanel::load_stl()
|
||||||
auto expolygons = mesh.horizontal_projection();
|
auto expolygons = mesh.horizontal_projection();
|
||||||
|
|
||||||
if (expolygons.size() == 0) {
|
if (expolygons.size() == 0) {
|
||||||
show_error(this, _L("The selected file contains no geometry."));
|
show_error(this, _(L("The selected file contains no geometry.")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (expolygons.size() > 1) {
|
if (expolygons.size() > 1) {
|
||||||
show_error(this, _L("The selected file contains several disjoint areas. This is not supported."));
|
show_error(this, _(L("The selected file contains several disjoint areas. This is not supported.")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ class BedShapeDialog : public wxDialog
|
||||||
{
|
{
|
||||||
BedShapePanel* m_panel;
|
BedShapePanel* m_panel;
|
||||||
public:
|
public:
|
||||||
BedShapeDialog(wxWindow* parent) : wxDialog(parent, wxID_ANY, _L("Bed Shape"),
|
BedShapeDialog(wxWindow* parent) : wxDialog(parent, wxID_ANY, _(L("Bed Shape")),
|
||||||
wxDefaultPosition, wxSize(350, 700), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER){}
|
wxDefaultPosition, wxSize(350, 700), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER){}
|
||||||
~BedShapeDialog(){ }
|
~BedShapeDialog(){ }
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,20 @@
|
||||||
|
|
||||||
namespace Slic3r { namespace GUI {
|
namespace Slic3r { namespace GUI {
|
||||||
|
|
||||||
|
wxString double_to_string(double const value)
|
||||||
|
{
|
||||||
|
int precision = 10 * value - int(10 * value) == 0 ? 1 : 2;
|
||||||
|
return value - int(value) == 0 ?
|
||||||
|
wxString::Format(_T("%i"), int(value)) :
|
||||||
|
wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None);
|
||||||
|
}
|
||||||
|
|
||||||
void Field::on_kill_focus(wxEvent& event) {
|
void Field::on_kill_focus(wxEvent& event) {
|
||||||
// Without this, there will be nasty focus bugs on Windows.
|
// Without this, there will be nasty focus bugs on Windows.
|
||||||
// Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all
|
// Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all
|
||||||
// non-command events to allow the default handling to take place."
|
// non-command events to allow the default handling to take place."
|
||||||
event.Skip();
|
event.Skip();
|
||||||
std::cerr << "calling Field::on_kill_focus from " << m_opt_id<< "\n";
|
// call the registered function if it is available
|
||||||
// call the registered function if it is available
|
|
||||||
if (m_on_kill_focus!=nullptr)
|
if (m_on_kill_focus!=nullptr)
|
||||||
m_on_kill_focus();
|
m_on_kill_focus();
|
||||||
}
|
}
|
||||||
|
@ -30,9 +37,9 @@ namespace Slic3r { namespace GUI {
|
||||||
wxString Field::get_tooltip_text(const wxString& default_string)
|
wxString Field::get_tooltip_text(const wxString& default_string)
|
||||||
{
|
{
|
||||||
wxString tooltip_text("");
|
wxString tooltip_text("");
|
||||||
wxString tooltip = wxString::FromUTF8(m_opt.tooltip.c_str());
|
wxString tooltip = L_str(m_opt.tooltip);
|
||||||
if (tooltip.length() > 0)
|
if (tooltip.length() > 0)
|
||||||
tooltip_text = tooltip + "(" + _L("default") + ": " +
|
tooltip_text = tooltip + "(" + _(L("default")) + ": " +
|
||||||
(boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") +
|
(boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") +
|
||||||
default_string + ")";
|
default_string + ")";
|
||||||
|
|
||||||
|
@ -45,7 +52,7 @@ namespace Slic3r { namespace GUI {
|
||||||
return std::regex_match(string, regex_pattern);
|
return std::regex_match(string, regex_pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::any Field::get_value_by_opt_type(wxString str, ConfigOptionType type)
|
boost::any Field::get_value_by_opt_type(wxString str)
|
||||||
{
|
{
|
||||||
boost::any ret_val;
|
boost::any ret_val;
|
||||||
switch (m_opt.type){
|
switch (m_opt.type){
|
||||||
|
@ -56,23 +63,17 @@ namespace Slic3r { namespace GUI {
|
||||||
case coPercents:
|
case coPercents:
|
||||||
case coFloats:
|
case coFloats:
|
||||||
case coFloat:{
|
case coFloat:{
|
||||||
if (m_opt.type == coPercent) str.RemoveLast();
|
if (m_opt.type == coPercent && str.Last() == '%')
|
||||||
|
str.RemoveLast();
|
||||||
double val;
|
double val;
|
||||||
str.ToCDouble(&val);
|
str.ToCDouble(&val);
|
||||||
ret_val = val;
|
ret_val = val;
|
||||||
break; }
|
break; }
|
||||||
case coString:
|
case coString:
|
||||||
case coStrings:
|
case coStrings:
|
||||||
|
case coFloatOrPercent:
|
||||||
ret_val = str.ToStdString();
|
ret_val = str.ToStdString();
|
||||||
break;
|
break;
|
||||||
case coFloatOrPercent:{
|
|
||||||
if (str.Last() == '%')
|
|
||||||
str.RemoveLast();
|
|
||||||
double val;
|
|
||||||
str.ToCDouble(&val);
|
|
||||||
ret_val = val;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -90,13 +91,9 @@ namespace Slic3r { namespace GUI {
|
||||||
switch (m_opt.type) {
|
switch (m_opt.type) {
|
||||||
case coFloatOrPercent:
|
case coFloatOrPercent:
|
||||||
{
|
{
|
||||||
if (static_cast<const ConfigOptionFloatOrPercent*>(m_opt.default_value)->percent)
|
text_value = double_to_string(m_opt.default_value->getFloat());
|
||||||
{
|
if (static_cast<const ConfigOptionFloatOrPercent*>(m_opt.default_value)->percent)
|
||||||
text_value = wxString::Format(_T("%i"), int(m_opt.default_value->getFloat()));
|
text_value += "%";
|
||||||
text_value += "%";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
text_value = wxNumberFormatter::ToString(m_opt.default_value->getFloat(), 2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case coPercent:
|
case coPercent:
|
||||||
|
@ -106,29 +103,15 @@ namespace Slic3r { namespace GUI {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case coPercents:
|
case coPercents:
|
||||||
{
|
case coFloats:
|
||||||
const ConfigOptionPercents *vec = static_cast<const ConfigOptionPercents*>(m_opt.default_value);
|
|
||||||
if (vec == nullptr || vec->empty()) break;
|
|
||||||
if (vec->size() > 1)
|
|
||||||
break;
|
|
||||||
double val = vec->get_at(0);
|
|
||||||
text_value = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case coFloat:
|
case coFloat:
|
||||||
{
|
{
|
||||||
double val = m_opt.default_value->getFloat();
|
double val = m_opt.type == coFloats ?
|
||||||
text_value = (val - int(val)) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
|
static_cast<const ConfigOptionFloats*>(m_opt.default_value)->get_at(0) :
|
||||||
break;
|
m_opt.type == coFloat ?
|
||||||
}
|
m_opt.default_value->getFloat() :
|
||||||
case coFloats:
|
static_cast<const ConfigOptionPercents*>(m_opt.default_value)->get_at(0);
|
||||||
{
|
text_value = double_to_string(val);
|
||||||
const ConfigOptionFloats *vec = static_cast<const ConfigOptionFloats*>(m_opt.default_value);
|
|
||||||
if (vec == nullptr || vec->empty()) break;
|
|
||||||
if (vec->size() > 1)
|
|
||||||
break;
|
|
||||||
double val = vec->get_at(0);
|
|
||||||
text_value = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case coString:
|
case coString:
|
||||||
|
@ -174,7 +157,7 @@ namespace Slic3r { namespace GUI {
|
||||||
boost::any TextCtrl::get_value()
|
boost::any TextCtrl::get_value()
|
||||||
{
|
{
|
||||||
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
|
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
|
||||||
boost::any ret_val = get_value_by_opt_type(ret_str, m_opt.type);
|
boost::any ret_val = get_value_by_opt_type(ret_str);
|
||||||
|
|
||||||
return ret_val;
|
return ret_val;
|
||||||
}
|
}
|
||||||
|
@ -303,7 +286,7 @@ void Choice::set_selection()
|
||||||
break;
|
break;
|
||||||
++idx;
|
++idx;
|
||||||
}
|
}
|
||||||
if (m_opt.type == coPercent) text_value += "%";
|
// if (m_opt.type == coPercent) text_value += "%";
|
||||||
idx == m_opt.enum_values.size() ?
|
idx == m_opt.enum_values.size() ?
|
||||||
dynamic_cast<wxComboBox*>(window)->SetValue(text_value) :
|
dynamic_cast<wxComboBox*>(window)->SetValue(text_value) :
|
||||||
dynamic_cast<wxComboBox*>(window)->SetSelection(idx);
|
dynamic_cast<wxComboBox*>(window)->SetSelection(idx);
|
||||||
|
@ -387,7 +370,7 @@ void Choice::set_value(boost::any value)
|
||||||
break;
|
break;
|
||||||
++idx;
|
++idx;
|
||||||
}
|
}
|
||||||
if (m_opt.type == coPercent) text_value += "%";
|
// if (m_opt.type == coPercent) text_value += "%";
|
||||||
idx == m_opt.enum_values.size() ?
|
idx == m_opt.enum_values.size() ?
|
||||||
dynamic_cast<wxComboBox*>(window)->SetValue(text_value) :
|
dynamic_cast<wxComboBox*>(window)->SetValue(text_value) :
|
||||||
dynamic_cast<wxComboBox*>(window)->SetSelection(idx);
|
dynamic_cast<wxComboBox*>(window)->SetSelection(idx);
|
||||||
|
@ -429,7 +412,7 @@ boost::any Choice::get_value()
|
||||||
wxString ret_str = static_cast<wxComboBox*>(window)->GetValue();
|
wxString ret_str = static_cast<wxComboBox*>(window)->GetValue();
|
||||||
|
|
||||||
if (m_opt.type != coEnum)
|
if (m_opt.type != coEnum)
|
||||||
ret_val = get_value_by_opt_type(ret_str, m_opt.type);
|
ret_val = get_value_by_opt_type(ret_str);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int ret_enum = static_cast<wxComboBox*>(window)->GetSelection();
|
int ret_enum = static_cast<wxComboBox*>(window)->GetSelection();
|
||||||
|
@ -535,21 +518,29 @@ void PointCtrl::set_value(const Pointf value)
|
||||||
void PointCtrl::set_value(boost::any value)
|
void PointCtrl::set_value(boost::any value)
|
||||||
{
|
{
|
||||||
Pointf pt;
|
Pointf pt;
|
||||||
try
|
Pointf *ptf = boost::any_cast<Pointf>(&value);
|
||||||
|
if (!ptf)
|
||||||
{
|
{
|
||||||
pt = boost::any_cast<ConfigOptionPoints*>(value)->values.at(0);
|
ConfigOptionPoints* pts = boost::any_cast<ConfigOptionPoints*>(value);
|
||||||
|
pt = pts->values.at(0);
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
else
|
||||||
{
|
pt = *ptf;
|
||||||
try{
|
// try
|
||||||
pt = boost::any_cast<Pointf>(value);
|
// {
|
||||||
}
|
// pt = boost::any_cast<ConfigOptionPoints*>(value)->values.at(0);
|
||||||
catch (const std::exception &e)
|
// }
|
||||||
{
|
// catch (const std::exception &e)
|
||||||
std::cerr << "Error! Can't cast PointCtrl value" << m_opt_id << "\n";
|
// {
|
||||||
return;
|
// try{
|
||||||
}
|
// pt = boost::any_cast<Pointf>(value);
|
||||||
}
|
// }
|
||||||
|
// catch (const std::exception &e)
|
||||||
|
// {
|
||||||
|
// std::cerr << "Error! Can't cast PointCtrl value" << m_opt_id << "\n";
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
set_value(pt);
|
set_value(pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,8 @@ using t_field = std::unique_ptr<Field>;
|
||||||
using t_kill_focus = std::function<void()>;
|
using t_kill_focus = std::function<void()>;
|
||||||
using t_change = std::function<void(t_config_option_key, boost::any)>;
|
using t_change = std::function<void(t_config_option_key, boost::any)>;
|
||||||
|
|
||||||
|
wxString double_to_string(double const value);
|
||||||
|
|
||||||
class Field {
|
class Field {
|
||||||
protected:
|
protected:
|
||||||
// factory function to defer and enforce creation of derived type.
|
// factory function to defer and enforce creation of derived type.
|
||||||
|
@ -83,7 +85,7 @@ public:
|
||||||
virtual wxWindow* getWindow() { return nullptr; }
|
virtual wxWindow* getWindow() { return nullptr; }
|
||||||
|
|
||||||
bool is_matched(std::string string, std::string pattern);
|
bool is_matched(std::string string, std::string pattern);
|
||||||
boost::any get_value_by_opt_type(wxString str, ConfigOptionType type);
|
boost::any get_value_by_opt_type(wxString str);
|
||||||
|
|
||||||
/// Factory method for generating new derived classes.
|
/// Factory method for generating new derived classes.
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
#include <boost/algorithm/string/classification.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
#import <IOKit/pwr_mgt/IOPMLib.h>
|
#import <IOKit/pwr_mgt/IOPMLib.h>
|
||||||
|
@ -28,7 +28,6 @@
|
||||||
|
|
||||||
#include <wx/app.h>
|
#include <wx/app.h>
|
||||||
#include <wx/button.h>
|
#include <wx/button.h>
|
||||||
#include <wx/config.h>
|
|
||||||
#include <wx/dir.h>
|
#include <wx/dir.h>
|
||||||
#include <wx/filename.h>
|
#include <wx/filename.h>
|
||||||
#include <wx/frame.h>
|
#include <wx/frame.h>
|
||||||
|
@ -45,6 +44,7 @@
|
||||||
#include "TabIface.hpp"
|
#include "TabIface.hpp"
|
||||||
#include "AppConfig.hpp"
|
#include "AppConfig.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
#include "Preferences.hpp"
|
||||||
|
|
||||||
namespace Slic3r { namespace GUI {
|
namespace Slic3r { namespace GUI {
|
||||||
|
|
||||||
|
@ -171,6 +171,7 @@ void break_to_debugger()
|
||||||
wxApp *g_wxApp = nullptr;
|
wxApp *g_wxApp = nullptr;
|
||||||
wxFrame *g_wxMainFrame = nullptr;
|
wxFrame *g_wxMainFrame = nullptr;
|
||||||
wxNotebook *g_wxTabPanel = nullptr;
|
wxNotebook *g_wxTabPanel = nullptr;
|
||||||
|
AppConfig *g_AppConfig = nullptr;
|
||||||
|
|
||||||
std::vector<Tab *> g_tabs_list;
|
std::vector<Tab *> g_tabs_list;
|
||||||
|
|
||||||
|
@ -191,6 +192,11 @@ void set_tab_panel(wxNotebook *tab_panel)
|
||||||
g_wxTabPanel = tab_panel;
|
g_wxTabPanel = tab_panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_app_config(AppConfig *app_config)
|
||||||
|
{
|
||||||
|
g_AppConfig = app_config;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Tab *>& get_tabs_list()
|
std::vector<Tab *>& get_tabs_list()
|
||||||
{
|
{
|
||||||
return g_tabs_list;
|
return g_tabs_list;
|
||||||
|
@ -215,7 +221,7 @@ bool select_language(wxArrayString & names,
|
||||||
wxArrayLong & identifiers)
|
wxArrayLong & identifiers)
|
||||||
{
|
{
|
||||||
wxCHECK_MSG(names.Count() == identifiers.Count(), false,
|
wxCHECK_MSG(names.Count() == identifiers.Count(), false,
|
||||||
_L("Array of language names and identifiers should have the same size."));
|
_(L("Array of language names and identifiers should have the same size.")));
|
||||||
int init_selection = 0;
|
int init_selection = 0;
|
||||||
long current_language = g_wxLocale ? g_wxLocale->GetLanguage() : wxLANGUAGE_UNKNOWN;
|
long current_language = g_wxLocale ? g_wxLocale->GetLanguage() : wxLANGUAGE_UNKNOWN;
|
||||||
for (auto lang : identifiers){
|
for (auto lang : identifiers){
|
||||||
|
@ -226,7 +232,7 @@ bool select_language(wxArrayString & names,
|
||||||
}
|
}
|
||||||
if (init_selection == identifiers.size())
|
if (init_selection == identifiers.size())
|
||||||
init_selection = 0;
|
init_selection = 0;
|
||||||
long index = wxGetSingleChoiceIndex(_L("Select the language"), _L("Language"),
|
long index = wxGetSingleChoiceIndex(_(L("Select the language")), _(L("Language")),
|
||||||
names, init_selection);
|
names, init_selection);
|
||||||
if (index != -1)
|
if (index != -1)
|
||||||
{
|
{
|
||||||
|
@ -241,13 +247,14 @@ bool select_language(wxArrayString & names,
|
||||||
|
|
||||||
bool load_language()
|
bool load_language()
|
||||||
{
|
{
|
||||||
wxConfig config(g_wxApp->GetAppName());
|
|
||||||
long language;
|
long language;
|
||||||
if (!config.Read(wxT("wxTranslation_Language"),
|
if (!g_AppConfig->has("translation_language"))
|
||||||
&language, wxLANGUAGE_UNKNOWN))
|
|
||||||
{
|
|
||||||
language = wxLANGUAGE_UNKNOWN;
|
language = wxLANGUAGE_UNKNOWN;
|
||||||
|
else {
|
||||||
|
auto str_language = g_AppConfig->get("translation_language");
|
||||||
|
language = str_language != "" ? stol(str_language) : wxLANGUAGE_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (language == wxLANGUAGE_UNKNOWN)
|
if (language == wxLANGUAGE_UNKNOWN)
|
||||||
return false;
|
return false;
|
||||||
wxArrayString names;
|
wxArrayString names;
|
||||||
|
@ -269,13 +276,13 @@ bool load_language()
|
||||||
|
|
||||||
void save_language()
|
void save_language()
|
||||||
{
|
{
|
||||||
wxConfig config(g_wxApp->GetAppName());
|
|
||||||
long language = wxLANGUAGE_UNKNOWN;
|
long language = wxLANGUAGE_UNKNOWN;
|
||||||
if (g_wxLocale) {
|
if (g_wxLocale) {
|
||||||
language = g_wxLocale->GetLanguage();
|
language = g_wxLocale->GetLanguage();
|
||||||
}
|
}
|
||||||
config.Write(wxT("wxTranslation_Language"), language);
|
std::string str_language = std::to_string(language);
|
||||||
config.Flush();
|
g_AppConfig->set("translation_language", str_language);
|
||||||
|
g_AppConfig->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_installed_languages(wxArrayString & names,
|
void get_installed_languages(wxArrayString & names,
|
||||||
|
@ -290,15 +297,12 @@ void get_installed_languages(wxArrayString & names,
|
||||||
wxString name = wxLocale::GetLanguageName(wxLANGUAGE_DEFAULT);
|
wxString name = wxLocale::GetLanguageName(wxLANGUAGE_DEFAULT);
|
||||||
if (!name.IsEmpty())
|
if (!name.IsEmpty())
|
||||||
{
|
{
|
||||||
names.Add(_L("Default"));
|
names.Add(_(L("Default")));
|
||||||
identifiers.Add(wxLANGUAGE_DEFAULT);
|
identifiers.Add(wxLANGUAGE_DEFAULT);
|
||||||
}
|
}
|
||||||
for (bool cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS);
|
for (bool cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS);
|
||||||
cont; cont = dir.GetNext(&filename))
|
cont; cont = dir.GetNext(&filename))
|
||||||
{
|
{
|
||||||
wxLogTrace(wxTraceMask(),
|
|
||||||
"L10n: Directory found = \"%s\"",
|
|
||||||
filename.GetData());
|
|
||||||
langinfo = wxLocale::FindLanguageInfo(filename);
|
langinfo = wxLocale::FindLanguageInfo(filename);
|
||||||
if (langinfo != NULL)
|
if (langinfo != NULL)
|
||||||
{
|
{
|
||||||
|
@ -318,33 +322,39 @@ void add_debug_menu(wxMenuBar *menu, int event_language_change)
|
||||||
{
|
{
|
||||||
//#if 0
|
//#if 0
|
||||||
auto local_menu = new wxMenu();
|
auto local_menu = new wxMenu();
|
||||||
local_menu->Append(wxWindow::NewControlId(1), _L("Change Application Language"));
|
local_menu->Append(wxWindow::NewControlId(1), _(L("Change Application Language")));
|
||||||
local_menu->Bind(wxEVT_MENU, [event_language_change](wxEvent&){
|
local_menu->Bind(wxEVT_MENU, [event_language_change](wxEvent&){
|
||||||
wxArrayString names;
|
wxArrayString names;
|
||||||
wxArrayLong identifiers;
|
wxArrayLong identifiers;
|
||||||
get_installed_languages(names, identifiers);
|
get_installed_languages(names, identifiers);
|
||||||
if (select_language(names, identifiers)){
|
if (select_language(names, identifiers)){
|
||||||
save_language();
|
save_language();
|
||||||
show_info(g_wxTabPanel, "Application will be restarted", "Attention!");
|
show_info(g_wxTabPanel, _(L("Application will be restarted")), _(L("Attention!")));
|
||||||
if (event_language_change > 0) {
|
if (event_language_change > 0) {
|
||||||
wxCommandEvent event(event_language_change);
|
wxCommandEvent event(event_language_change);
|
||||||
g_wxApp->ProcessEvent(event);
|
g_wxApp->ProcessEvent(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
menu->Append(local_menu, _T("&Localization"));
|
menu->Append(local_menu, _(L("&Localization")));
|
||||||
//#endif
|
//#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config,
|
void open_preferences_dialog(int event_preferences)
|
||||||
|
{
|
||||||
|
auto dlg = new PreferencesDialog(g_wxMainFrame, event_preferences);
|
||||||
|
dlg->ShowModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_preset_tabs(PresetBundle *preset_bundle,
|
||||||
bool no_controller, bool is_disabled_button_browse, bool is_user_agent,
|
bool no_controller, bool is_disabled_button_browse, bool is_user_agent,
|
||||||
int event_value_change, int event_presets_changed,
|
int event_value_change, int event_presets_changed,
|
||||||
int event_button_browse, int event_button_test)
|
int event_button_browse, int event_button_test)
|
||||||
{
|
{
|
||||||
add_created_tab(new TabPrint (g_wxTabPanel, no_controller), preset_bundle, app_config);
|
add_created_tab(new TabPrint (g_wxTabPanel, no_controller), preset_bundle);
|
||||||
add_created_tab(new TabFilament (g_wxTabPanel, no_controller), preset_bundle, app_config);
|
add_created_tab(new TabFilament (g_wxTabPanel, no_controller), preset_bundle);
|
||||||
add_created_tab(new TabPrinter (g_wxTabPanel, no_controller, is_disabled_button_browse, is_user_agent),
|
add_created_tab(new TabPrinter (g_wxTabPanel, no_controller, is_disabled_button_browse, is_user_agent),
|
||||||
preset_bundle, app_config);
|
preset_bundle);
|
||||||
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
|
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) {
|
||||||
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
|
Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i));
|
||||||
if (! tab)
|
if (! tab)
|
||||||
|
@ -378,8 +388,14 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b
|
||||||
try{
|
try{
|
||||||
switch (config.def()->get(opt_key)->type){
|
switch (config.def()->get(opt_key)->type){
|
||||||
case coFloatOrPercent:{
|
case coFloatOrPercent:{
|
||||||
const auto &val = *config.option<ConfigOptionFloatOrPercent>(opt_key);
|
std::string str = boost::any_cast<std::string>(value);
|
||||||
config.set_key_value(opt_key, new ConfigOptionFloatOrPercent(boost::any_cast<double>(value), val.percent));
|
bool percent = false;
|
||||||
|
if (str.back() == '%'){
|
||||||
|
str.pop_back();
|
||||||
|
percent = true;
|
||||||
|
}
|
||||||
|
double val = stod(str);
|
||||||
|
config.set_key_value(opt_key, new ConfigOptionFloatOrPercent(val, percent));
|
||||||
break;}
|
break;}
|
||||||
case coPercent:
|
case coPercent:
|
||||||
config.set_key_value(opt_key, new ConfigOptionPercent(boost::any_cast<double>(value)));
|
config.set_key_value(opt_key, new ConfigOptionPercent(boost::any_cast<double>(value)));
|
||||||
|
@ -389,11 +405,15 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b
|
||||||
val = boost::any_cast<double>(value);
|
val = boost::any_cast<double>(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case coPercents:
|
case coPercents:{
|
||||||
case coFloats:{
|
ConfigOptionPercents* vec_new = new ConfigOptionPercents{ boost::any_cast<double>(value) };
|
||||||
double& val = config.opt_float(opt_key, 0);
|
config.option<ConfigOptionPercents>(opt_key)->set_at(vec_new, opt_index, opt_index);
|
||||||
val = boost::any_cast<double>(value);
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
case coFloats:{
|
||||||
|
ConfigOptionFloats* vec_new = new ConfigOptionFloats{ boost::any_cast<double>(value) };
|
||||||
|
config.option<ConfigOptionFloats>(opt_key)->set_at(vec_new, opt_index, opt_index);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case coString:
|
case coString:
|
||||||
config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value)));
|
config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value)));
|
||||||
|
@ -406,7 +426,7 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
ConfigOptionStrings* vec_new = new ConfigOptionStrings{ boost::any_cast<std::string>(value) };
|
ConfigOptionStrings* vec_new = new ConfigOptionStrings{ boost::any_cast<std::string>(value) };
|
||||||
config.option<ConfigOptionStrings>(opt_key)->set_at(vec_new, opt_index, opt_index);
|
config.option<ConfigOptionStrings>(opt_key)->set_at(vec_new, opt_index, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -415,14 +435,14 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b
|
||||||
break;
|
break;
|
||||||
case coBools:{
|
case coBools:{
|
||||||
ConfigOptionBools* vec_new = new ConfigOptionBools{ boost::any_cast<bool>(value) };
|
ConfigOptionBools* vec_new = new ConfigOptionBools{ boost::any_cast<bool>(value) };
|
||||||
config.option<ConfigOptionBools>(opt_key)->set_at(vec_new, opt_index, opt_index);
|
config.option<ConfigOptionBools>(opt_key)->set_at(vec_new, opt_index, 0);
|
||||||
break;}
|
break;}
|
||||||
case coInt:
|
case coInt:
|
||||||
config.set_key_value(opt_key, new ConfigOptionInt(boost::any_cast<int>(value)));
|
config.set_key_value(opt_key, new ConfigOptionInt(boost::any_cast<int>(value)));
|
||||||
break;
|
break;
|
||||||
case coInts:{
|
case coInts:{
|
||||||
ConfigOptionInts* vec_new = new ConfigOptionInts{ boost::any_cast<int>(value) };
|
ConfigOptionInts* vec_new = new ConfigOptionInts{ boost::any_cast<int>(value) };
|
||||||
config.option<ConfigOptionInts>(opt_key)->set_at(vec_new, opt_index, opt_index);
|
config.option<ConfigOptionInts>(opt_key)->set_at(vec_new, opt_index, 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case coEnum:{
|
case coEnum:{
|
||||||
|
@ -455,9 +475,8 @@ void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_created_tab(Tab* panel, PresetBundle *preset_bundle, AppConfig *app_config)
|
void add_created_tab(Tab* panel, PresetBundle *preset_bundle)
|
||||||
{
|
{
|
||||||
panel->m_show_btn_incompatible_presets = app_config->get("show_incompatible_presets").empty();
|
|
||||||
panel->create_preset_tab(preset_bundle);
|
panel->create_preset_tab(preset_bundle);
|
||||||
|
|
||||||
// Load the currently selected preset into the GUI, update the preset selection box.
|
// Load the currently selected preset into the GUI, update the preset selection box.
|
||||||
|
@ -466,15 +485,22 @@ void add_created_tab(Tab* panel, PresetBundle *preset_bundle, AppConfig *app_con
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_error(wxWindow* parent, wxString message){
|
void show_error(wxWindow* parent, wxString message){
|
||||||
auto msg_wingow = new wxMessageDialog(parent, message, _L("Error"), wxOK | wxICON_ERROR);
|
auto msg_wingow = new wxMessageDialog(parent, message, _(L("Error")), wxOK | wxICON_ERROR);
|
||||||
msg_wingow->ShowModal();
|
msg_wingow->ShowModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_info(wxWindow* parent, wxString message, wxString title){
|
void show_info(wxWindow* parent, wxString message, wxString title){
|
||||||
auto msg_wingow = new wxMessageDialog(parent, message, title.empty() ? _L("Notice") : title, wxOK | wxICON_INFORMATION);
|
auto msg_wingow = new wxMessageDialog(parent, message, title.empty() ? _(L("Notice")) : title, wxOK | wxICON_INFORMATION);
|
||||||
msg_wingow->ShowModal();
|
msg_wingow->ShowModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void warning_catcher(wxWindow* parent, wxString message){
|
||||||
|
if (message == _(L("GLUquadricObjPtr | Attempt to free unreferenced scalar")) )
|
||||||
|
return;
|
||||||
|
auto msg = new wxMessageDialog(parent, message, _(L("Warning")), wxOK | wxICON_WARNING);
|
||||||
|
msg->ShowModal();
|
||||||
|
}
|
||||||
|
|
||||||
wxApp* get_app(){
|
wxApp* get_app(){
|
||||||
return g_wxApp;
|
return g_wxApp;
|
||||||
}
|
}
|
||||||
|
@ -487,17 +513,24 @@ void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string
|
||||||
wxCheckListBoxComboPopup* popup = new wxCheckListBoxComboPopup;
|
wxCheckListBoxComboPopup* popup = new wxCheckListBoxComboPopup;
|
||||||
if (popup != nullptr)
|
if (popup != nullptr)
|
||||||
{
|
{
|
||||||
|
// FIXME If the following line is removed, the combo box popup list will not react to mouse clicks.
|
||||||
|
// On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10.
|
||||||
|
comboCtrl->UseAltPopupWindow();
|
||||||
|
|
||||||
|
comboCtrl->EnablePopupAnimation(false);
|
||||||
comboCtrl->SetPopupControl(popup);
|
comboCtrl->SetPopupControl(popup);
|
||||||
popup->SetStringValue(text);
|
popup->SetStringValue(from_u8(text));
|
||||||
popup->Connect(wxID_ANY, wxEVT_CHECKLISTBOX, wxCommandEventHandler(wxCheckListBoxComboPopup::OnCheckListBox), nullptr, popup);
|
popup->Bind(wxEVT_CHECKLISTBOX, [popup](wxCommandEvent& evt) { popup->OnCheckListBox(evt); });
|
||||||
popup->Connect(wxID_ANY, wxEVT_LISTBOX, wxCommandEventHandler(wxCheckListBoxComboPopup::OnListBoxSelection), nullptr, popup);
|
popup->Bind(wxEVT_LISTBOX, [popup](wxCommandEvent& evt) { popup->OnListBoxSelection(evt); });
|
||||||
|
popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
|
||||||
|
popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); });
|
||||||
|
|
||||||
std::vector<std::string> items_str;
|
std::vector<std::string> items_str;
|
||||||
boost::split(items_str, items, boost::is_any_of("|"), boost::token_compress_off);
|
boost::split(items_str, items, boost::is_any_of("|"), boost::token_compress_off);
|
||||||
|
|
||||||
for (const std::string& item : items_str)
|
for (const std::string& item : items_str)
|
||||||
{
|
{
|
||||||
popup->Append(item);
|
popup->Append(from_u8(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < popup->GetCount(); ++i)
|
for (unsigned int i = 0; i < popup->GetCount(); ++i)
|
||||||
|
@ -524,4 +557,34 @@ int combochecklist_get_flags(wxComboCtrl* comboCtrl)
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppConfig* get_app_config()
|
||||||
|
{
|
||||||
|
return g_AppConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString L_str(std::string str)
|
||||||
|
{
|
||||||
|
//! Explicitly specify that the source string is already in UTF-8 encoding
|
||||||
|
return wxGetTranslation(wxString(str.c_str(), wxConvUTF8));
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString from_u8(std::string str)
|
||||||
|
{
|
||||||
|
return wxString::FromUTF8(str.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
wxWindow *get_widget_by_id(int id)
|
||||||
|
{
|
||||||
|
if (g_wxMainFrame == nullptr) {
|
||||||
|
throw std::runtime_error("Main frame not set");
|
||||||
|
}
|
||||||
|
|
||||||
|
wxWindow *window = g_wxMainFrame->FindWindow(id);
|
||||||
|
if (window == nullptr) {
|
||||||
|
throw std::runtime_error((boost::format("Could not find widget by ID: %1%") % id).str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
} }
|
} }
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "Config.hpp"
|
#include "Config.hpp"
|
||||||
|
|
||||||
class wxApp;
|
class wxApp;
|
||||||
|
class wxWindow;
|
||||||
class wxFrame;
|
class wxFrame;
|
||||||
class wxWindow;
|
class wxWindow;
|
||||||
class wxMenuBar;
|
class wxMenuBar;
|
||||||
|
@ -23,10 +24,19 @@ class AppConfig;
|
||||||
class DynamicPrintConfig;
|
class DynamicPrintConfig;
|
||||||
class TabIface;
|
class TabIface;
|
||||||
|
|
||||||
//! macro used to localization, return wxString
|
// !!! If you needed to translate some wxString,
|
||||||
#define _L(s) wxGetTranslation(s)
|
// !!! please use _(L(string))
|
||||||
//! macro used to localization, return const CharType *
|
// !!! _() - is a standard wxWidgets macro to translate
|
||||||
#define _LU8(s) wxGetTranslation(s).ToUTF8().data()
|
// !!! L() is used only for marking localizable string
|
||||||
|
// !!! It will be used in "xgettext" to create a Locating Message Catalog.
|
||||||
|
#define L(s) s
|
||||||
|
|
||||||
|
//! macro used to localization, return wxScopedCharBuffer
|
||||||
|
//! With wxConvUTF8 explicitly specify that the source string is already in UTF-8 encoding
|
||||||
|
#define _CHB(s) wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str()
|
||||||
|
|
||||||
|
// Minimal buffer length for translated string (char buf[MIN_BUF_LENGTH_FOR_L])
|
||||||
|
#define MIN_BUF_LENGTH_FOR_L 128
|
||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
|
@ -39,8 +49,9 @@ inline t_file_wild_card& get_file_wild_card() {
|
||||||
FILE_WILDCARDS["known"] = "Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA";
|
FILE_WILDCARDS["known"] = "Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA";
|
||||||
FILE_WILDCARDS["stl"] = "STL files (*.stl)|*.stl;*.STL";
|
FILE_WILDCARDS["stl"] = "STL files (*.stl)|*.stl;*.STL";
|
||||||
FILE_WILDCARDS["obj"] = "OBJ files (*.obj)|*.obj;*.OBJ";
|
FILE_WILDCARDS["obj"] = "OBJ files (*.obj)|*.obj;*.OBJ";
|
||||||
FILE_WILDCARDS["amf"] = "AMF files (*.amf)|*.amf;*.AMF;*.xml;*.XML";
|
FILE_WILDCARDS["amf"] = "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML";
|
||||||
FILE_WILDCARDS["prusa"] = "Prusa Control files (*.prusa)|*.prusa;*.PRUSA";
|
FILE_WILDCARDS["3mf"] = "3MF files (*.3mf)|*.3mf;*.3MF;";
|
||||||
|
FILE_WILDCARDS["prusa"] = "Prusa Control files (*.prusa)|*.prusa;*.PRUSA";
|
||||||
FILE_WILDCARDS["ini"] = "INI files *.ini|*.ini;*.INI";
|
FILE_WILDCARDS["ini"] = "INI files *.ini|*.ini;*.INI";
|
||||||
FILE_WILDCARDS["gcode"] = "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC";
|
FILE_WILDCARDS["gcode"] = "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC";
|
||||||
FILE_WILDCARDS["svg"] = "SVG files *.svg|*.svg;*.SVG";
|
FILE_WILDCARDS["svg"] = "SVG files *.svg|*.svg;*.SVG";
|
||||||
|
@ -58,22 +69,31 @@ void break_to_debugger();
|
||||||
void set_wxapp(wxApp *app);
|
void set_wxapp(wxApp *app);
|
||||||
void set_main_frame(wxFrame *main_frame);
|
void set_main_frame(wxFrame *main_frame);
|
||||||
void set_tab_panel(wxNotebook *tab_panel);
|
void set_tab_panel(wxNotebook *tab_panel);
|
||||||
|
void set_app_config(AppConfig *app_config);
|
||||||
|
|
||||||
|
AppConfig* get_app_config();
|
||||||
|
wxApp* get_app();
|
||||||
|
|
||||||
void add_debug_menu(wxMenuBar *menu, int event_language_change);
|
void add_debug_menu(wxMenuBar *menu, int event_language_change);
|
||||||
|
|
||||||
|
// Create "Preferences" dialog after selecting menu "Preferences" in Perl part
|
||||||
|
void open_preferences_dialog(int event_preferences);
|
||||||
|
|
||||||
// Create a new preset tab (print, filament and printer),
|
// Create a new preset tab (print, filament and printer),
|
||||||
void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config,
|
void create_preset_tabs(PresetBundle *preset_bundle,
|
||||||
bool no_controller, bool is_disabled_button_browse, bool is_user_agent,
|
bool no_controller, bool is_disabled_button_browse, bool is_user_agent,
|
||||||
int event_value_change, int event_presets_changed,
|
int event_value_change, int event_presets_changed,
|
||||||
int event_button_browse, int event_button_test);
|
int event_button_browse, int event_button_test);
|
||||||
TabIface* get_preset_tab_iface(char *name);
|
TabIface* get_preset_tab_iface(char *name);
|
||||||
|
|
||||||
// add it at the end of the tab panel.
|
// add it at the end of the tab panel.
|
||||||
void add_created_tab(Tab* panel, PresetBundle *preset_bundle, AppConfig *app_config);
|
void add_created_tab(Tab* panel, PresetBundle *preset_bundle);
|
||||||
// Change option value in config
|
// Change option value in config
|
||||||
void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, boost::any value, int opt_index = 0);
|
void change_opt_value(DynamicPrintConfig& config, t_config_option_key opt_key, boost::any value, int opt_index = 0);
|
||||||
|
|
||||||
void show_error(wxWindow* parent, wxString message);
|
void show_error(wxWindow* parent, wxString message);
|
||||||
void show_info(wxWindow* parent, wxString message, wxString title);
|
void show_info(wxWindow* parent, wxString message, wxString title);
|
||||||
|
void warning_catcher(wxWindow* parent, wxString message);
|
||||||
|
|
||||||
// load language saved at application config
|
// load language saved at application config
|
||||||
bool load_language();
|
bool load_language();
|
||||||
|
@ -97,6 +117,13 @@ void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string
|
||||||
// encoded inside an int.
|
// encoded inside an int.
|
||||||
int combochecklist_get_flags(wxComboCtrl* comboCtrl);
|
int combochecklist_get_flags(wxComboCtrl* comboCtrl);
|
||||||
|
|
||||||
|
// Return translated std::string as a wxString
|
||||||
|
wxString L_str(std::string str);
|
||||||
|
// Return wxString from std::string in UTF8
|
||||||
|
wxString from_u8(std::string str);
|
||||||
|
|
||||||
|
wxWindow *get_widget_by_id(int id);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
#include "ConfigExceptions.hpp"
|
#include "ConfigExceptions.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <wx/tooltip.h>
|
|
||||||
#include <wx/numformatter.h>
|
#include <wx/numformatter.h>
|
||||||
|
|
||||||
namespace Slic3r { namespace GUI {
|
namespace Slic3r { namespace GUI {
|
||||||
|
@ -122,13 +121,13 @@ void OptionsGroup::append_line(const Line& line) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's a widget, build it and add the result to the sizer.
|
// If there's a widget, build it and add the result to the sizer.
|
||||||
if (line.widget != nullptr) {
|
if (line.widget != nullptr) {
|
||||||
auto wgt = line.widget(parent());
|
auto wgt = line.widget(parent());
|
||||||
grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, wxOSX ? 0 : 5);
|
grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, wxOSX ? 0 : 5);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we have a single option with no sidetext just add it directly to the grid sizer
|
// if we have a single option with no sidetext just add it directly to the grid sizer
|
||||||
if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 &&
|
if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 &&
|
||||||
option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) {
|
option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) {
|
||||||
const auto& option = option_set.front();
|
const auto& option = option_set.front();
|
||||||
|
@ -152,7 +151,12 @@ void OptionsGroup::append_line(const Line& line) {
|
||||||
ConfigOptionDef option = opt.opt;
|
ConfigOptionDef option = opt.opt;
|
||||||
// add label if any
|
// add label if any
|
||||||
if (option.label != "") {
|
if (option.label != "") {
|
||||||
auto field_label = new wxStaticText(parent(), wxID_ANY, wxString::FromUTF8(option.label.c_str()) + ":", wxDefaultPosition, wxDefaultSize);
|
wxString str_label = L_str(option.label);
|
||||||
|
//! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
|
||||||
|
// wxString str_label = (option.label == "Top" || option.label == "Bottom") ?
|
||||||
|
// wxGETTEXT_IN_CONTEXT("Layers", wxString(option.label.c_str()):
|
||||||
|
// L_str(option.label);
|
||||||
|
auto field_label = new wxStaticText(parent(), wxID_ANY, str_label + ":", wxDefaultPosition, wxDefaultSize);
|
||||||
field_label->SetFont(label_font);
|
field_label->SetFont(label_font);
|
||||||
sizer->Add(field_label, 0, wxALIGN_CENTER_VERTICAL, 0);
|
sizer->Add(field_label, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||||
}
|
}
|
||||||
|
@ -166,7 +170,7 @@ void OptionsGroup::append_line(const Line& line) {
|
||||||
|
|
||||||
// add sidetext if any
|
// add sidetext if any
|
||||||
if (option.sidetext != "") {
|
if (option.sidetext != "") {
|
||||||
auto sidetext = new wxStaticText(parent(), wxID_ANY, wxString::FromUTF8(option.sidetext.c_str()), wxDefaultPosition, wxDefaultSize);
|
auto sidetext = new wxStaticText(parent(), wxID_ANY, L_str(option.sidetext), wxDefaultPosition, wxDefaultSize);
|
||||||
sidetext->SetFont(sidetext_font);
|
sidetext->SetFont(sidetext_font);
|
||||||
sizer->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
|
sizer->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
|
||||||
}
|
}
|
||||||
|
@ -188,7 +192,7 @@ void OptionsGroup::append_line(const Line& line) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Line OptionsGroup::create_single_option_line(const Option& option) const {
|
Line OptionsGroup::create_single_option_line(const Option& option) const {
|
||||||
Line retval{ wxString::FromUTF8(option.opt.label.c_str()), wxString::FromUTF8(option.opt.tooltip.c_str()) };
|
Line retval{ L_str(option.opt.label), L_str(option.opt.tooltip) };
|
||||||
Option tmp(option);
|
Option tmp(option);
|
||||||
tmp.opt.label = std::string("");
|
tmp.opt.label = std::string("");
|
||||||
retval.append_option(tmp);
|
retval.append_option(tmp);
|
||||||
|
@ -203,7 +207,7 @@ void OptionsGroup::on_change_OG(t_config_option_key id, /*config_value*/boost::a
|
||||||
Option ConfigOptionsGroup::get_option(const std::string opt_key, int opt_index /*= -1*/)
|
Option ConfigOptionsGroup::get_option(const std::string opt_key, int opt_index /*= -1*/)
|
||||||
{
|
{
|
||||||
if (!m_config->has(opt_key)) {
|
if (!m_config->has(opt_key)) {
|
||||||
//! exception ("No $opt_key in ConfigOptionsGroup config");
|
std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string opt_id = opt_index == -1 ? opt_key : opt_key + "#" + std::to_string(opt_index);
|
std::string opt_id = opt_index == -1 ? opt_key : opt_key + "#" + std::to_string(opt_index);
|
||||||
|
@ -287,14 +291,6 @@ boost::any ConfigOptionsGroup::config_value(std::string opt_key, int opt_index,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString double_to_string(double const value)
|
|
||||||
{
|
|
||||||
int precision = 10 * value - int(10 * value) == 0 ? 1 : 2;
|
|
||||||
return value - int(value) == 0 ?
|
|
||||||
wxString::Format(_T("%i"), int(value)) :
|
|
||||||
wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None);
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::any ConfigOptionsGroup::get_config_value(DynamicPrintConfig& config, std::string opt_key, int opt_index/* = -1*/)
|
boost::any ConfigOptionsGroup::get_config_value(DynamicPrintConfig& config, std::string opt_key, int opt_index/* = -1*/)
|
||||||
{
|
{
|
||||||
size_t idx = opt_index == -1 ? 0 : opt_index;
|
size_t idx = opt_index == -1 ? 0 : opt_index;
|
||||||
|
@ -325,9 +321,9 @@ boost::any ConfigOptionsGroup::get_config_value(DynamicPrintConfig& config, std:
|
||||||
case coFloats:
|
case coFloats:
|
||||||
case coFloat:{
|
case coFloat:{
|
||||||
double val = opt->type == coFloats ?
|
double val = opt->type == coFloats ?
|
||||||
config.opt_float(opt_key, idx/*0opt_index*/) :
|
config.opt_float(opt_key, idx) :
|
||||||
opt->type == coFloat ? config.opt_float(opt_key) :
|
opt->type == coFloat ? config.opt_float(opt_key) :
|
||||||
config.option<ConfigOptionPercents>(opt_key)->values.at(idx/*0*/);
|
config.option<ConfigOptionPercents>(opt_key)->values.at(idx);
|
||||||
ret = double_to_string(val);
|
ret = double_to_string(val);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -338,19 +334,19 @@ boost::any ConfigOptionsGroup::get_config_value(DynamicPrintConfig& config, std:
|
||||||
if (config.option<ConfigOptionStrings>(opt_key)->values.empty())
|
if (config.option<ConfigOptionStrings>(opt_key)->values.empty())
|
||||||
ret = text_value;
|
ret = text_value;
|
||||||
else
|
else
|
||||||
ret = static_cast<wxString>(config.opt_string(opt_key, static_cast<unsigned int>(idx/*0*/)/*opt_index*/));
|
ret = static_cast<wxString>(config.opt_string(opt_key, static_cast<unsigned int>(idx)));
|
||||||
break;
|
break;
|
||||||
case coBool:
|
case coBool:
|
||||||
ret = config.opt_bool(opt_key);
|
ret = config.opt_bool(opt_key);
|
||||||
break;
|
break;
|
||||||
case coBools:
|
case coBools:
|
||||||
ret = config.opt_bool(opt_key, idx/*0opt_index*/);
|
ret = config.opt_bool(opt_key, idx);
|
||||||
break;
|
break;
|
||||||
case coInt:
|
case coInt:
|
||||||
ret = config.opt_int(opt_key);
|
ret = config.opt_int(opt_key);
|
||||||
break;
|
break;
|
||||||
case coInts:
|
case coInts:
|
||||||
ret = config.opt_int(opt_key, idx/*0/*opt_index*/);
|
ret = config.opt_int(opt_key, idx);
|
||||||
break;
|
break;
|
||||||
case coEnum:{
|
case coEnum:{
|
||||||
if (opt_key.compare("external_fill_pattern") == 0 ||
|
if (opt_key.compare("external_fill_pattern") == 0 ||
|
||||||
|
@ -369,7 +365,7 @@ boost::any ConfigOptionsGroup::get_config_value(DynamicPrintConfig& config, std:
|
||||||
break;
|
break;
|
||||||
case coPoints:{
|
case coPoints:{
|
||||||
const auto &value = *config.option<ConfigOptionPoints>(opt_key);
|
const auto &value = *config.option<ConfigOptionPoints>(opt_key);
|
||||||
ret = value.values.at(idx/*0*/);
|
ret = value.values.at(idx);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case coNone:
|
case coNone:
|
||||||
|
@ -397,14 +393,5 @@ void ogStaticText::SetText(wxString value)
|
||||||
GetParent()->Layout();
|
GetParent()->Layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Option::translate()
|
|
||||||
{
|
|
||||||
opt.label = _LU8(opt.label);
|
|
||||||
opt.tooltip = _LU8(opt.tooltip);
|
|
||||||
opt.sidetext = _LU8(opt.sidetext);
|
|
||||||
opt.full_label = _LU8(opt.full_label);
|
|
||||||
opt.category = _LU8(opt.category);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // GUI
|
} // GUI
|
||||||
} // Slic3r
|
} // Slic3r
|
||||||
|
|
|
@ -35,8 +35,7 @@ struct Option {
|
||||||
bool readonly {false};
|
bool readonly {false};
|
||||||
|
|
||||||
Option(const ConfigOptionDef& _opt, t_config_option_key id) :
|
Option(const ConfigOptionDef& _opt, t_config_option_key id) :
|
||||||
opt(_opt), opt_id(id) { translate(); }
|
opt(_opt), opt_id(id) {}
|
||||||
void translate();
|
|
||||||
};
|
};
|
||||||
using t_option = std::unique_ptr<Option>; //!
|
using t_option = std::unique_ptr<Option>; //!
|
||||||
|
|
||||||
|
@ -90,9 +89,22 @@ public:
|
||||||
void append_single_option_line(const Option& option) { append_line(create_single_option_line(option)); }
|
void append_single_option_line(const Option& option) { append_line(create_single_option_line(option)); }
|
||||||
|
|
||||||
// return a non-owning pointer reference
|
// return a non-owning pointer reference
|
||||||
inline /*const*/ Field* get_field(t_config_option_key id) const { try { return m_fields.at(id).get(); } catch (std::out_of_range e) { return nullptr; } }
|
inline Field* get_field(t_config_option_key id) const{
|
||||||
bool set_value(t_config_option_key id, boost::any value) { try { m_fields.at(id)->set_value(value); return true; } catch (std::out_of_range e) { return false; } }
|
if (m_fields.find(id) == m_fields.end()) return nullptr;
|
||||||
boost::any get_value(t_config_option_key id) { boost::any out; try { out = m_fields.at(id)->get_value(); } catch (std::out_of_range e) { ; } return out; }
|
return m_fields.at(id).get();
|
||||||
|
}
|
||||||
|
bool set_value(t_config_option_key id, boost::any value) {
|
||||||
|
if (m_fields.find(id) == m_fields.end()) return false;
|
||||||
|
m_fields.at(id)->set_value(value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
boost::any get_value(t_config_option_key id) {
|
||||||
|
boost::any out;
|
||||||
|
if (m_fields.find(id) == m_fields.end()) ;
|
||||||
|
else
|
||||||
|
out = m_fields.at(id)->get_value();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
inline void enable() { for (auto& field : m_fields) field.second->enable(); }
|
inline void enable() { for (auto& field : m_fields) field.second->enable(); }
|
||||||
inline void disable() { for (auto& field : m_fields) field.second->disable(); }
|
inline void disable() { for (auto& field : m_fields) field.second->disable(); }
|
||||||
|
|
120
xs/src/slic3r/GUI/Preferences.cpp
Normal file
120
xs/src/slic3r/GUI/Preferences.cpp
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
#include "Preferences.hpp"
|
||||||
|
#include "AppConfig.hpp"
|
||||||
|
#include "OptionsGroup.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
void PreferencesDialog::build()
|
||||||
|
{
|
||||||
|
auto app_config = get_app_config();
|
||||||
|
m_optgroup = std::make_shared<ConfigOptionsGroup>(this, _(L("General")));
|
||||||
|
m_optgroup->label_width = 200;
|
||||||
|
m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
|
||||||
|
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
|
||||||
|
};
|
||||||
|
|
||||||
|
// $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||||
|
// opt_id = > 'version_check',
|
||||||
|
// type = > 'bool',
|
||||||
|
// label = > 'Check for updates',
|
||||||
|
// tooltip = > 'If this is enabled, Slic3r will check for updates daily and display a reminder if a newer version is available.',
|
||||||
|
// default = > $app_config->get("version_check") // 1,
|
||||||
|
// readonly = > !wxTheApp->have_version_check,
|
||||||
|
// ));
|
||||||
|
|
||||||
|
ConfigOptionDef def;
|
||||||
|
def.label = L("Remember output directory");
|
||||||
|
def.type = coBool;
|
||||||
|
def.tooltip = L("If this is enabled, Slic3r will prompt the last output directory "
|
||||||
|
"instead of the one containing the input files.");
|
||||||
|
def.default_value = new ConfigOptionBool{ app_config->get("remember_output_path")[0] == '1' }; // 1;
|
||||||
|
Option option(def, "remember_output_path");
|
||||||
|
m_optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
|
def.label = L("Auto-center parts");
|
||||||
|
def.type = coBool;
|
||||||
|
def.tooltip = L("If this is enabled, Slic3r will auto-center objects "
|
||||||
|
"around the print bed center.");
|
||||||
|
def.default_value = new ConfigOptionBool{ app_config->get("autocenter")[0] == '1' }; // 1;
|
||||||
|
option = Option (def,"autocenter");
|
||||||
|
m_optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
|
def.label = L("Background processing");
|
||||||
|
def.type = coBool;
|
||||||
|
def.tooltip = L("If this is enabled, Slic3r will pre-process objects as soon "
|
||||||
|
"as they\'re loaded in order to save time when exporting G-code.");
|
||||||
|
def.default_value = new ConfigOptionBool{ app_config->get("background_processing")[0] == '1' }; // 1;
|
||||||
|
option = Option (def,"background_processing");
|
||||||
|
m_optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
|
def.label = L("Disable USB/serial connection");
|
||||||
|
def.type = coBool;
|
||||||
|
def.tooltip = L("Disable communication with the printer over a serial / USB cable. "
|
||||||
|
"This simplifies the user interface in case the printer is never attached to the computer.");
|
||||||
|
def.default_value = new ConfigOptionBool{ app_config->get("no_controller")[0] == '1' }; // 1;
|
||||||
|
option = Option (def,"no_controller");
|
||||||
|
m_optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
|
def.label = L("Suppress \" - default - \" presets");
|
||||||
|
def.type = coBool;
|
||||||
|
def.tooltip = L("Suppress \" - default - \" presets in the Print / Filament / Printer "
|
||||||
|
"selections once there are any other valid presets available.");
|
||||||
|
def.default_value = new ConfigOptionBool{ app_config->get("no_defaults")[0] == '1' }; // 1;
|
||||||
|
option = Option (def,"no_defaults");
|
||||||
|
m_optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
|
def.label = L("Show incompatible print and filament presets");
|
||||||
|
def.type = coBool;
|
||||||
|
def.tooltip = L("When checked, the print and filament presets are shown in the preset editor "
|
||||||
|
"even if they are marked as incompatible with the active printer");
|
||||||
|
def.default_value = new ConfigOptionBool{ app_config->get("show_incompatible_presets")[0] == '1' }; // 1;
|
||||||
|
option = Option (def,"show_incompatible_presets");
|
||||||
|
m_optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
|
def.label = L("Use legacy OpenGL 1.1 rendering");
|
||||||
|
def.type = coBool;
|
||||||
|
def.tooltip = L("If you have rendering issues caused by a buggy OpenGL 2.0 driver, "
|
||||||
|
"you may try to check this checkbox. This will disable the layer height "
|
||||||
|
"editing and anti aliasing, so it is likely better to upgrade your graphics driver.");
|
||||||
|
def.default_value = new ConfigOptionBool{ app_config->get("use_legacy_opengl")[0] == '1' }; // 1;
|
||||||
|
option = Option (def,"use_legacy_opengl");
|
||||||
|
m_optgroup->append_single_option_line(option);
|
||||||
|
|
||||||
|
auto sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
sizer->Add(m_optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||||
|
|
||||||
|
auto buttons = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
|
||||||
|
wxButton* btn = static_cast<wxButton*>(FindWindowById(wxID_OK, this));
|
||||||
|
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); });
|
||||||
|
sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10);
|
||||||
|
|
||||||
|
SetSizer(sizer);
|
||||||
|
sizer->SetSizeHints(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreferencesDialog::accept()
|
||||||
|
{
|
||||||
|
if (m_values.find("no_controller") != m_values.end()||
|
||||||
|
m_values.find("no_defaults") != m_values.end()||
|
||||||
|
m_values.find("use_legacy_opengl")!= m_values.end()) {
|
||||||
|
warning_catcher(this, _(L("You need to restart Slic3r to make the changes effective.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto app_config = get_app_config();
|
||||||
|
for (std::map<std::string, std::string>::iterator it = m_values.begin(); it != m_values.end(); ++it) {
|
||||||
|
app_config->set(it->first, it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
EndModal(wxID_OK);
|
||||||
|
Close(); // needed on Linux
|
||||||
|
|
||||||
|
// Nothify the UI to update itself from the ini file.
|
||||||
|
if (m_event_preferences > 0) {
|
||||||
|
wxCommandEvent event(m_event_preferences);
|
||||||
|
get_app()->ProcessEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // GUI
|
||||||
|
} // Slic3r
|
27
xs/src/slic3r/GUI/Preferences.hpp
Normal file
27
xs/src/slic3r/GUI/Preferences.hpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#include "GUI.hpp"
|
||||||
|
|
||||||
|
#include <wx/dialog.h>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
class ConfigOptionsGroup;
|
||||||
|
|
||||||
|
class PreferencesDialog : public wxDialog
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string> m_values;
|
||||||
|
std::shared_ptr<ConfigOptionsGroup> m_optgroup;
|
||||||
|
int m_event_preferences;
|
||||||
|
public:
|
||||||
|
PreferencesDialog(wxWindow* parent, int event_preferences) : wxDialog(parent, wxID_ANY, _(L("Preferences")),
|
||||||
|
wxDefaultPosition, wxDefaultSize), m_event_preferences(event_preferences) { build(); }
|
||||||
|
~PreferencesDialog(){ }
|
||||||
|
|
||||||
|
void build();
|
||||||
|
void accept();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // GUI
|
||||||
|
} // Slic3r
|
||||||
|
|
|
@ -224,7 +224,7 @@ const std::vector<std::string>& Preset::printer_options()
|
||||||
if (s_opts.empty()) {
|
if (s_opts.empty()) {
|
||||||
s_opts = {
|
s_opts = {
|
||||||
"bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
|
"bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
|
||||||
"octoprint_host", "octoprint_apikey", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
|
"octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
|
||||||
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
|
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
|
||||||
"between_objects_gcode", "printer_notes", "cooling_tube_retraction", "cooling_tube_length", "parking_pos_retraction"
|
"between_objects_gcode", "printer_notes", "cooling_tube_retraction", "cooling_tube_length", "parking_pos_retraction"
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
#include <boost/property_tree/ini_parser.hpp>
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
#include <boost/locale.hpp>
|
#include <boost/locale.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
#include <wx/dcmemory.h>
|
#include <wx/dcmemory.h>
|
||||||
#include <wx/image.h>
|
#include <wx/image.h>
|
||||||
|
@ -474,6 +475,125 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const
|
||||||
this->update_compatible_with_printer(false);
|
this->update_compatible_with_printer(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process the Config Bundle loaded as a Boost property tree.
|
||||||
|
// For each print, filament and printer preset (group defined by group_name), apply the inherited presets.
|
||||||
|
// The presets starting with '*' are considered non-terminal and they are
|
||||||
|
// removed through the flattening process by this function.
|
||||||
|
// This function will never fail, but it will produce error messages through boost::log.
|
||||||
|
static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree, const std::string &group_name)
|
||||||
|
{
|
||||||
|
namespace pt = boost::property_tree;
|
||||||
|
|
||||||
|
typedef std::pair<pt::ptree::key_type, pt::ptree> ptree_child_type;
|
||||||
|
|
||||||
|
// 1) For the group given by group_name, initialize the presets.
|
||||||
|
struct Prst {
|
||||||
|
Prst(const std::string &name, pt::ptree *node) : name(name), node(node) {}
|
||||||
|
// Name of this preset. If the name starts with '*', it is an intermediate preset,
|
||||||
|
// which will not make it into the result.
|
||||||
|
const std::string name;
|
||||||
|
// Link to the source boost property tree node, owned by tree.
|
||||||
|
pt::ptree *node;
|
||||||
|
// Link to the presets, from which this preset inherits.
|
||||||
|
std::vector<Prst*> inherits;
|
||||||
|
// Link to the presets, for which this preset is a direct parent.
|
||||||
|
std::vector<Prst*> parent_of;
|
||||||
|
// When running the Kahn's Topological sorting algorithm, this counter is decreased from inherits.size() to zero.
|
||||||
|
// A cycle is indicated, if the number does not drop to zero after the Kahn's algorithm finishes.
|
||||||
|
size_t num_incoming_edges_left = 0;
|
||||||
|
// Sorting by the name, to be used when inserted into std::set.
|
||||||
|
bool operator==(const Prst &rhs) const { return this->name == rhs.name; }
|
||||||
|
bool operator< (const Prst &rhs) const { return this->name < rhs.name; }
|
||||||
|
};
|
||||||
|
// Find the presets, store them into a std::map, addressed by their names.
|
||||||
|
std::set<Prst> presets;
|
||||||
|
std::string group_name_preset = group_name + ":";
|
||||||
|
for (auto §ion : tree)
|
||||||
|
if (boost::starts_with(section.first, group_name_preset) && section.first.size() > group_name_preset.size())
|
||||||
|
presets.emplace(section.first.substr(group_name_preset.size()), §ion.second);
|
||||||
|
// Fill in the "inherits" and "parent_of" members, report invalid inheritance fields.
|
||||||
|
for (const Prst &prst : presets) {
|
||||||
|
// Parse the list of comma separated values, possibly enclosed in quotes.
|
||||||
|
std::vector<std::string> inherits_names;
|
||||||
|
if (Slic3r::unescape_strings_cstyle(prst.node->get<std::string>("inherits", ""), inherits_names)) {
|
||||||
|
// Resolve the inheritance by name.
|
||||||
|
std::vector<Prst*> &inherits_nodes = const_cast<Prst&>(prst).inherits;
|
||||||
|
for (const std::string &node_name : inherits_names) {
|
||||||
|
auto it = presets.find(Prst(node_name, nullptr));
|
||||||
|
if (it == presets.end())
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "flatten_configbundle_hierarchy: The preset " << prst.name << " inherits an unknown preset \"" << node_name << "\"";
|
||||||
|
else {
|
||||||
|
inherits_nodes.emplace_back(const_cast<Prst*>(&(*it)));
|
||||||
|
inherits_nodes.back()->parent_of.emplace_back(const_cast<Prst*>(&prst));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "flatten_configbundle_hierarchy: The preset " << prst.name << " has an invalid \"inherits\" field";
|
||||||
|
}
|
||||||
|
// Remove the "inherits" key, it has no meaning outside the config bundle.
|
||||||
|
const_cast<pt::ptree*>(prst.node)->erase("inherits");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Create a linear ordering for the directed acyclic graph of preset inheritance.
|
||||||
|
// https://en.wikipedia.org/wiki/Topological_sorting
|
||||||
|
// Kahn's algorithm.
|
||||||
|
std::vector<Prst*> sorted;
|
||||||
|
{
|
||||||
|
// Initialize S with the set of all nodes with no incoming edge.
|
||||||
|
std::deque<Prst*> S;
|
||||||
|
for (const Prst &prst : presets)
|
||||||
|
if (prst.inherits.empty())
|
||||||
|
S.emplace_back(const_cast<Prst*>(&prst));
|
||||||
|
else
|
||||||
|
const_cast<Prst*>(&prst)->num_incoming_edges_left = prst.inherits.size();
|
||||||
|
while (! S.empty()) {
|
||||||
|
Prst *n = S.front();
|
||||||
|
S.pop_front();
|
||||||
|
sorted.emplace_back(n);
|
||||||
|
for (Prst *m : n->parent_of) {
|
||||||
|
assert(m->num_incoming_edges_left > 0);
|
||||||
|
if (-- m->num_incoming_edges_left == 0) {
|
||||||
|
// We have visited all parents of m.
|
||||||
|
S.emplace_back(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sorted.size() < presets.size()) {
|
||||||
|
for (const Prst &prst : presets)
|
||||||
|
if (prst.num_incoming_edges_left)
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "flatten_configbundle_hierarchy: The preset " << prst.name << " has cyclic dependencies";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the dependencies in their topological ordering.
|
||||||
|
for (Prst *prst : sorted) {
|
||||||
|
// Merge the preset nodes in their order of application.
|
||||||
|
// Iterate in a reverse order, so the last change will be placed first in merged.
|
||||||
|
for (auto it_inherits = prst->inherits.rbegin(); it_inherits != prst->inherits.rend(); ++ it_inherits)
|
||||||
|
for (auto it = (*it_inherits)->node->begin(); it != (*it_inherits)->node->end(); ++ it)
|
||||||
|
if (prst->node->find(it->first) == prst->node->not_found())
|
||||||
|
prst->node->add_child(it->first, it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the "internal" presets from the ptree. These presets are marked with '*'.
|
||||||
|
group_name_preset += '*';
|
||||||
|
for (auto it_section = tree.begin(); it_section != tree.end(); ) {
|
||||||
|
if (boost::starts_with(it_section->first, group_name_preset) && it_section->first.size() > group_name_preset.size())
|
||||||
|
// Remove the "internal" preset from the ptree.
|
||||||
|
it_section = tree.erase(it_section);
|
||||||
|
else
|
||||||
|
// Keep the preset.
|
||||||
|
++ it_section;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree)
|
||||||
|
{
|
||||||
|
flatten_configbundle_hierarchy(tree, "print");
|
||||||
|
flatten_configbundle_hierarchy(tree, "filament");
|
||||||
|
flatten_configbundle_hierarchy(tree, "printer");
|
||||||
|
}
|
||||||
|
|
||||||
// Load a config bundle file, into presets and store the loaded presets into separate files
|
// Load a config bundle file, into presets and store the loaded presets into separate files
|
||||||
// of the local configuration directory.
|
// of the local configuration directory.
|
||||||
size_t PresetBundle::load_configbundle(const std::string &path, unsigned int flags)
|
size_t PresetBundle::load_configbundle(const std::string &path, unsigned int flags)
|
||||||
|
@ -486,6 +606,8 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
||||||
pt::ptree tree;
|
pt::ptree tree;
|
||||||
boost::nowide::ifstream ifs(path);
|
boost::nowide::ifstream ifs(path);
|
||||||
pt::read_ini(ifs, tree);
|
pt::read_ini(ifs, tree);
|
||||||
|
// Flatten the config bundle by applying the inheritance rules. Internal profiles (with names starting with '*') are removed.
|
||||||
|
flatten_configbundle_hierarchy(tree);
|
||||||
|
|
||||||
// 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files.
|
// 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files.
|
||||||
std::vector<std::string> loaded_prints;
|
std::vector<std::string> loaded_prints;
|
||||||
|
|
|
@ -6,8 +6,10 @@
|
||||||
#include "Flow.hpp"
|
#include "Flow.hpp"
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <wx/intl.h>
|
||||||
|
|
||||||
#include "../../libslic3r/libslic3r.h"
|
#include "../../libslic3r/libslic3r.h"
|
||||||
|
#include "GUI.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -21,31 +23,31 @@ std::string PresetHints::cooling_description(const Preset &preset)
|
||||||
int max_fan_speed = preset.config.opt_int("max_fan_speed", 0);
|
int max_fan_speed = preset.config.opt_int("max_fan_speed", 0);
|
||||||
int min_print_speed = int(preset.config.opt_float("min_print_speed", 0) + 0.5);
|
int min_print_speed = int(preset.config.opt_float("min_print_speed", 0) + 0.5);
|
||||||
int fan_below_layer_time = preset.config.opt_int("fan_below_layer_time", 0);
|
int fan_below_layer_time = preset.config.opt_int("fan_below_layer_time", 0);
|
||||||
sprintf(buf, "If estimated layer time is below ~%ds, fan will run at %d%% and print speed will be reduced so that no less than %ds are spent on that layer (however, speed will never be reduced below %dmm/s).",
|
sprintf(buf, _CHB(L("If estimated layer time is below ~%ds, fan will run at %d%% and print speed will be reduced so that no less than %ds are spent on that layer (however, speed will never be reduced below %dmm/s).")),
|
||||||
slowdown_below_layer_time, max_fan_speed, slowdown_below_layer_time, min_print_speed);
|
slowdown_below_layer_time, max_fan_speed, slowdown_below_layer_time, min_print_speed);
|
||||||
out += buf;
|
out += buf;
|
||||||
if (fan_below_layer_time > slowdown_below_layer_time) {
|
if (fan_below_layer_time > slowdown_below_layer_time) {
|
||||||
sprintf(buf, "\nIf estimated layer time is greater, but still below ~%ds, fan will run at a proportionally decreasing speed between %d%% and %d%%.",
|
sprintf(buf, _CHB(L("\nIf estimated layer time is greater, but still below ~%ds, fan will run at a proportionally decreasing speed between %d%% and %d%%.")),
|
||||||
fan_below_layer_time, max_fan_speed, min_fan_speed);
|
fan_below_layer_time, max_fan_speed, min_fan_speed);
|
||||||
out += buf;
|
out += buf;
|
||||||
}
|
}
|
||||||
out += "\nDuring the other layers, fan ";
|
out += _CHB(L("\nDuring the other layers, fan "));
|
||||||
} else {
|
} else {
|
||||||
out = "Fan ";
|
out = _CHB(L("Fan "));
|
||||||
}
|
}
|
||||||
if (preset.config.opt_bool("fan_always_on", 0)) {
|
if (preset.config.opt_bool("fan_always_on", 0)) {
|
||||||
int disable_fan_first_layers = preset.config.opt_int("disable_fan_first_layers", 0);
|
int disable_fan_first_layers = preset.config.opt_int("disable_fan_first_layers", 0);
|
||||||
int min_fan_speed = preset.config.opt_int("min_fan_speed", 0);
|
int min_fan_speed = preset.config.opt_int("min_fan_speed", 0);
|
||||||
sprintf(buf, "will always run at %d%% ", min_fan_speed);
|
sprintf(buf, _CHB(L("will always run at %d%% ")), min_fan_speed);
|
||||||
out += buf;
|
out += buf;
|
||||||
if (disable_fan_first_layers > 1) {
|
if (disable_fan_first_layers > 1) {
|
||||||
sprintf(buf, "except for the first %d layers", disable_fan_first_layers);
|
sprintf(buf, _CHB(L("except for the first %d layers")), disable_fan_first_layers);
|
||||||
out += buf;
|
out += buf;
|
||||||
}
|
}
|
||||||
else if (disable_fan_first_layers == 1)
|
else if (disable_fan_first_layers == 1)
|
||||||
out += "except for the first layer";
|
out += _CHB(L("except for the first layer"));
|
||||||
} else
|
} else
|
||||||
out += "will be turned off.";
|
out += _CHB(L("will be turned off."));
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@ -146,7 +148,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||||
limit_by_first_layer_speed(std::max(external_perimeter_speed, small_perimeter_speed), max_print_speed));
|
limit_by_first_layer_speed(std::max(external_perimeter_speed, small_perimeter_speed), max_print_speed));
|
||||||
if (max_flow < external_perimeter_rate) {
|
if (max_flow < external_perimeter_rate) {
|
||||||
max_flow = external_perimeter_rate;
|
max_flow = external_perimeter_rate;
|
||||||
max_flow_extrusion_type = "external perimeters";
|
max_flow_extrusion_type = _CHB(L("external perimeters"));
|
||||||
}
|
}
|
||||||
double perimeter_rate = Flow::new_from_config_width(frPerimeter,
|
double perimeter_rate = Flow::new_from_config_width(frPerimeter,
|
||||||
first_positive(first_layer_extrusion_width_ptr, perimeter_extrusion_width, extrusion_width),
|
first_positive(first_layer_extrusion_width_ptr, perimeter_extrusion_width, extrusion_width),
|
||||||
|
@ -155,7 +157,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||||
limit_by_first_layer_speed(std::max(perimeter_speed, small_perimeter_speed), max_print_speed));
|
limit_by_first_layer_speed(std::max(perimeter_speed, small_perimeter_speed), max_print_speed));
|
||||||
if (max_flow < perimeter_rate) {
|
if (max_flow < perimeter_rate) {
|
||||||
max_flow = perimeter_rate;
|
max_flow = perimeter_rate;
|
||||||
max_flow_extrusion_type = "perimeters";
|
max_flow_extrusion_type = _CHB(L("perimeters"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! bridging && infill_extruder_active) {
|
if (! bridging && infill_extruder_active) {
|
||||||
|
@ -164,7 +166,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||||
nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(infill_speed, max_print_speed);
|
nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(infill_speed, max_print_speed);
|
||||||
if (max_flow < infill_rate) {
|
if (max_flow < infill_rate) {
|
||||||
max_flow = infill_rate;
|
max_flow = infill_rate;
|
||||||
max_flow_extrusion_type = "infill";
|
max_flow_extrusion_type = _CHB(L("infill"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (solid_infill_extruder_active) {
|
if (solid_infill_extruder_active) {
|
||||||
|
@ -174,7 +176,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||||
(bridging ? bridge_speed : limit_by_first_layer_speed(solid_infill_speed, max_print_speed));
|
(bridging ? bridge_speed : limit_by_first_layer_speed(solid_infill_speed, max_print_speed));
|
||||||
if (max_flow < solid_infill_rate) {
|
if (max_flow < solid_infill_rate) {
|
||||||
max_flow = solid_infill_rate;
|
max_flow = solid_infill_rate;
|
||||||
max_flow_extrusion_type = "solid infill";
|
max_flow_extrusion_type = _CHB(L("solid infill"));
|
||||||
}
|
}
|
||||||
if (! bridging) {
|
if (! bridging) {
|
||||||
double top_solid_infill_rate = Flow::new_from_config_width(frInfill,
|
double top_solid_infill_rate = Flow::new_from_config_width(frInfill,
|
||||||
|
@ -182,7 +184,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||||
nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(top_solid_infill_speed, max_print_speed);
|
nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(top_solid_infill_speed, max_print_speed);
|
||||||
if (max_flow < top_solid_infill_rate) {
|
if (max_flow < top_solid_infill_rate) {
|
||||||
max_flow = top_solid_infill_rate;
|
max_flow = top_solid_infill_rate;
|
||||||
max_flow_extrusion_type = "top solid infill";
|
max_flow_extrusion_type = _CHB(L("top solid infill"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,7 +195,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||||
(bridging ? bridge_speed : limit_by_first_layer_speed(support_material_speed, max_print_speed));
|
(bridging ? bridge_speed : limit_by_first_layer_speed(support_material_speed, max_print_speed));
|
||||||
if (max_flow < support_material_rate) {
|
if (max_flow < support_material_rate) {
|
||||||
max_flow = support_material_rate;
|
max_flow = support_material_rate;
|
||||||
max_flow_extrusion_type = "support";
|
max_flow_extrusion_type = _CHB(L("support"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (support_material_interface_extruder_active) {
|
if (support_material_interface_extruder_active) {
|
||||||
|
@ -203,25 +205,25 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||||
(bridging ? bridge_speed : limit_by_first_layer_speed(support_material_interface_speed, max_print_speed));
|
(bridging ? bridge_speed : limit_by_first_layer_speed(support_material_interface_speed, max_print_speed));
|
||||||
if (max_flow < support_material_interface_rate) {
|
if (max_flow < support_material_interface_rate) {
|
||||||
max_flow = support_material_interface_rate;
|
max_flow = support_material_interface_rate;
|
||||||
max_flow_extrusion_type = "support interface";
|
max_flow_extrusion_type = _CHB(L("support interface"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//FIXME handle gap_fill_speed
|
//FIXME handle gap_fill_speed
|
||||||
if (! out.empty())
|
if (! out.empty())
|
||||||
out += "\n";
|
out += "\n";
|
||||||
out += (first_layer ? "First layer volumetric" : (bridging ? "Bridging volumetric" : "Volumetric"));
|
out += (first_layer ? _CHB(L("First layer volumetric")) : (bridging ? _CHB(L("Bridging volumetric")) : _CHB(L("Volumetric"))));
|
||||||
out += " flow rate is maximized ";
|
out += _CHB(L(" flow rate is maximized "));
|
||||||
bool limited_by_max_volumetric_speed = max_volumetric_speed > 0 && max_volumetric_speed < max_flow;
|
bool limited_by_max_volumetric_speed = max_volumetric_speed > 0 && max_volumetric_speed < max_flow;
|
||||||
out += (limited_by_max_volumetric_speed ?
|
out += (limited_by_max_volumetric_speed ?
|
||||||
"by the print profile maximum" :
|
_CHB(L("by the print profile maximum")) :
|
||||||
("when printing " + max_flow_extrusion_type))
|
(_CHB(L("when printing ")) + max_flow_extrusion_type))
|
||||||
+ " with a volumetric rate ";
|
+ _CHB(L(" with a volumetric rate "));
|
||||||
if (limited_by_max_volumetric_speed)
|
if (limited_by_max_volumetric_speed)
|
||||||
max_flow = max_volumetric_speed;
|
max_flow = max_volumetric_speed;
|
||||||
char buf[2048];
|
char buf[2048];
|
||||||
sprintf(buf, "%3.2f mm³/s", max_flow);
|
sprintf(buf, _CHB(L("%3.2f mm³/s")), max_flow);
|
||||||
out += buf;
|
out += buf;
|
||||||
sprintf(buf, " at filament speed %3.2f mm/s.", max_flow / filament_crossection);
|
sprintf(buf, _CHB(L(" at filament speed %3.2f mm/s.")), max_flow / filament_crossection);
|
||||||
out += buf;
|
out += buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,8 +240,11 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre
|
||||||
bool thin_walls = print_config.opt_bool("thin_walls");
|
bool thin_walls = print_config.opt_bool("thin_walls");
|
||||||
float nozzle_diameter = float(printer_config.opt_float("nozzle_diameter", 0));
|
float nozzle_diameter = float(printer_config.opt_float("nozzle_diameter", 0));
|
||||||
|
|
||||||
if (layer_height <= 0.f)
|
std::string out;
|
||||||
return "Recommended object thin wall thickness: Not available due to invalid layer height.";
|
if (layer_height <= 0.f){
|
||||||
|
out += _CHB(L("Recommended object thin wall thickness: Not available due to invalid layer height."));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
Flow external_perimeter_flow = Flow::new_from_config_width(
|
Flow external_perimeter_flow = Flow::new_from_config_width(
|
||||||
frExternalPerimeter,
|
frExternalPerimeter,
|
||||||
|
@ -250,18 +255,18 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre
|
||||||
*print_config.opt<ConfigOptionFloatOrPercent>("perimeter_extrusion_width"),
|
*print_config.opt<ConfigOptionFloatOrPercent>("perimeter_extrusion_width"),
|
||||||
nozzle_diameter, layer_height, false);
|
nozzle_diameter, layer_height, false);
|
||||||
|
|
||||||
std::string out;
|
|
||||||
if (num_perimeters > 0) {
|
if (num_perimeters > 0) {
|
||||||
int num_lines = std::min(num_perimeters * 2, 10);
|
int num_lines = std::min(num_perimeters * 2, 10);
|
||||||
char buf[256];
|
char buf[256];
|
||||||
sprintf(buf, "Recommended object thin wall thickness for layer height %.2f and ", layer_height);
|
sprintf(buf, _CHB(L("Recommended object thin wall thickness for layer height %.2f and ")), layer_height);
|
||||||
out += buf;
|
out += buf;
|
||||||
// Start with the width of two closely spaced
|
// Start with the width of two closely spaced
|
||||||
double width = external_perimeter_flow.width + external_perimeter_flow.spacing();
|
double width = external_perimeter_flow.width + external_perimeter_flow.spacing();
|
||||||
for (int i = 2; i <= num_lines; thin_walls ? ++ i : i += 2) {
|
for (int i = 2; i <= num_lines; thin_walls ? ++ i : i += 2) {
|
||||||
if (i > 2)
|
if (i > 2)
|
||||||
out += ", ";
|
out += ", ";
|
||||||
sprintf(buf, "%d lines: %.2lf mm", i, width);
|
sprintf(buf, _CHB(L("%d lines: %.2lf mm")), i, width);
|
||||||
out += buf;
|
out += buf;
|
||||||
width += perimeter_flow.spacing() * (thin_walls ? 1.f : 2.f);
|
width += perimeter_flow.spacing() * (thin_walls ? 1.f : 2.f);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -107,7 +107,7 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PresetBundle* m_preset_bundle;
|
PresetBundle* m_preset_bundle;
|
||||||
bool m_show_btn_incompatible_presets;
|
bool m_show_btn_incompatible_presets = false;
|
||||||
PresetCollection* m_presets;
|
PresetCollection* m_presets;
|
||||||
DynamicPrintConfig* m_config;
|
DynamicPrintConfig* m_config;
|
||||||
|
|
||||||
|
@ -180,7 +180,7 @@ class TabPrint : public Tab
|
||||||
public:
|
public:
|
||||||
TabPrint() {}
|
TabPrint() {}
|
||||||
TabPrint(wxNotebook* parent, bool no_controller) :
|
TabPrint(wxNotebook* parent, bool no_controller) :
|
||||||
Tab(parent, _L("Print Settings"), "print", no_controller) {}
|
Tab(parent, _(L("Print Settings")), "print", no_controller) {}
|
||||||
~TabPrint(){}
|
~TabPrint(){}
|
||||||
|
|
||||||
ogStaticText* m_recommended_thin_wall_thickness_description_line;
|
ogStaticText* m_recommended_thin_wall_thickness_description_line;
|
||||||
|
@ -200,7 +200,7 @@ class TabFilament : public Tab
|
||||||
public:
|
public:
|
||||||
TabFilament() {}
|
TabFilament() {}
|
||||||
TabFilament(wxNotebook* parent, bool no_controller) :
|
TabFilament(wxNotebook* parent, bool no_controller) :
|
||||||
Tab(parent, _L("Filament Settings"), "filament", no_controller) {}
|
Tab(parent, _(L("Filament Settings")), "filament", no_controller) {}
|
||||||
~TabFilament(){}
|
~TabFilament(){}
|
||||||
|
|
||||||
void build() override;
|
void build() override;
|
||||||
|
@ -226,7 +226,7 @@ public:
|
||||||
|
|
||||||
TabPrinter() {}
|
TabPrinter() {}
|
||||||
TabPrinter(wxNotebook* parent, bool no_controller, bool is_disabled_btn_browse, bool is_user_agent) :
|
TabPrinter(wxNotebook* parent, bool no_controller, bool is_disabled_btn_browse, bool is_user_agent) :
|
||||||
Tab(parent, _L("Printer Settings"), "printer", no_controller),
|
Tab(parent, _(L("Printer Settings")), "printer", no_controller),
|
||||||
m_is_disabled_button_browse(is_disabled_btn_browse),
|
m_is_disabled_button_browse(is_disabled_btn_browse),
|
||||||
m_is_user_agent(is_user_agent) {}
|
m_is_user_agent(is_user_agent) {}
|
||||||
~TabPrinter(){}
|
~TabPrinter(){}
|
||||||
|
@ -246,7 +246,7 @@ public:
|
||||||
class SavePresetWindow :public wxDialog
|
class SavePresetWindow :public wxDialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SavePresetWindow(wxWindow* parent) :wxDialog(parent, wxID_ANY, _L("Save preset")){}
|
SavePresetWindow(wxWindow* parent) :wxDialog(parent, wxID_ANY, _(L("Save preset"))){}
|
||||||
~SavePresetWindow(){}
|
~SavePresetWindow(){}
|
||||||
|
|
||||||
std::string m_chosen_name;
|
std::string m_chosen_name;
|
||||||
|
|
|
@ -11,7 +11,7 @@ void TabIface::load_config(DynamicPrintConfig* config) { m_tab->load_config(*con
|
||||||
void TabIface::load_key_value(char* opt_key, char* value){ m_tab->load_key_value(opt_key, static_cast<std::string>(value)); }
|
void TabIface::load_key_value(char* opt_key, char* value){ m_tab->load_key_value(opt_key, static_cast<std::string>(value)); }
|
||||||
bool TabIface::current_preset_is_dirty() { return m_tab->current_preset_is_dirty();}
|
bool TabIface::current_preset_is_dirty() { return m_tab->current_preset_is_dirty();}
|
||||||
void TabIface::OnActivate() { return m_tab->OnActivate();}
|
void TabIface::OnActivate() { return m_tab->OnActivate();}
|
||||||
std::string TabIface::title() { return m_tab->title().ToStdString();}
|
std::string TabIface::title() { return m_tab->title().ToUTF8().data(); }
|
||||||
DynamicPrintConfig* TabIface::get_config() { return m_tab->get_config(); }
|
DynamicPrintConfig* TabIface::get_config() { return m_tab->get_config(); }
|
||||||
PresetCollection* TabIface::get_presets() { return m_tab!=nullptr ? m_tab->get_presets() : nullptr; }
|
PresetCollection* TabIface::get_presets() { return m_tab!=nullptr ? m_tab->get_presets() : nullptr; }
|
||||||
std::vector<std::string> TabIface::get_dependent_tabs() { return m_tab->get_dependent_tabs(); }
|
std::vector<std::string> TabIface::get_dependent_tabs() { return m_tab->get_dependent_tabs(); }
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "wxExtensions.hpp"
|
#include "wxExtensions.hpp"
|
||||||
|
|
||||||
const unsigned int wxCheckListBoxComboPopup::Height = 210;
|
const unsigned int wxCheckListBoxComboPopup::DefaultWidth = 200;
|
||||||
|
const unsigned int wxCheckListBoxComboPopup::DefaultHeight = 200;
|
||||||
|
const unsigned int wxCheckListBoxComboPopup::DefaultItemHeight = 18;
|
||||||
|
|
||||||
bool wxCheckListBoxComboPopup::Create(wxWindow* parent)
|
bool wxCheckListBoxComboPopup::Create(wxWindow* parent)
|
||||||
{
|
{
|
||||||
|
@ -25,16 +27,55 @@ wxString wxCheckListBoxComboPopup::GetStringValue() const
|
||||||
wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
|
wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight)
|
||||||
{
|
{
|
||||||
// matches owner wxComboCtrl's width
|
// matches owner wxComboCtrl's width
|
||||||
|
// and sets height dinamically in dependence of contained items count
|
||||||
|
|
||||||
wxComboCtrl* cmb = GetComboCtrl();
|
wxComboCtrl* cmb = GetComboCtrl();
|
||||||
if (cmb != nullptr)
|
if (cmb != nullptr)
|
||||||
{
|
{
|
||||||
wxSize size = GetComboCtrl()->GetSize();
|
wxSize size = GetComboCtrl()->GetSize();
|
||||||
size.SetHeight(Height);
|
|
||||||
|
unsigned int count = GetCount();
|
||||||
|
if (count > 0)
|
||||||
|
size.SetHeight(count * DefaultItemHeight);
|
||||||
|
else
|
||||||
|
size.SetHeight(DefaultHeight);
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return wxSize(200, Height);
|
return wxSize(DefaultWidth, DefaultHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wxCheckListBoxComboPopup::OnKeyEvent(wxKeyEvent& evt)
|
||||||
|
{
|
||||||
|
// filters out all the keys which are not working properly
|
||||||
|
switch (evt.GetKeyCode())
|
||||||
|
{
|
||||||
|
case WXK_LEFT:
|
||||||
|
case WXK_UP:
|
||||||
|
case WXK_RIGHT:
|
||||||
|
case WXK_DOWN:
|
||||||
|
case WXK_PAGEUP:
|
||||||
|
case WXK_PAGEDOWN:
|
||||||
|
case WXK_END:
|
||||||
|
case WXK_HOME:
|
||||||
|
case WXK_NUMPAD_LEFT:
|
||||||
|
case WXK_NUMPAD_UP:
|
||||||
|
case WXK_NUMPAD_RIGHT:
|
||||||
|
case WXK_NUMPAD_DOWN:
|
||||||
|
case WXK_NUMPAD_PAGEUP:
|
||||||
|
case WXK_NUMPAD_PAGEDOWN:
|
||||||
|
case WXK_NUMPAD_END:
|
||||||
|
case WXK_NUMPAD_HOME:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
evt.Skip();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxCheckListBoxComboPopup::OnCheckListBox(wxCommandEvent& evt)
|
void wxCheckListBoxComboPopup::OnCheckListBox(wxCommandEvent& evt)
|
||||||
|
@ -48,6 +89,8 @@ void wxCheckListBoxComboPopup::OnCheckListBox(wxCommandEvent& evt)
|
||||||
event.SetEventObject(cmb);
|
event.SetEventObject(cmb);
|
||||||
cmb->ProcessWindowEvent(event);
|
cmb->ProcessWindowEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evt.Skip();
|
||||||
}
|
}
|
||||||
|
|
||||||
void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt)
|
void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt)
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
|
class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
|
||||||
{
|
{
|
||||||
static const unsigned int Height;
|
static const unsigned int DefaultWidth;
|
||||||
|
static const unsigned int DefaultHeight;
|
||||||
|
static const unsigned int DefaultItemHeight;
|
||||||
|
|
||||||
wxString m_text;
|
wxString m_text;
|
||||||
|
|
||||||
|
@ -17,6 +19,8 @@ public:
|
||||||
virtual wxString GetStringValue() const;
|
virtual wxString GetStringValue() const;
|
||||||
virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight);
|
virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight);
|
||||||
|
|
||||||
|
virtual void OnKeyEvent(wxKeyEvent& evt);
|
||||||
|
|
||||||
void OnCheckListBox(wxCommandEvent& evt);
|
void OnCheckListBox(wxCommandEvent& evt);
|
||||||
void OnListBoxSelection(wxCommandEvent& evt);
|
void OnListBoxSelection(wxCommandEvent& evt);
|
||||||
};
|
};
|
||||||
|
|
704
xs/src/slic3r/Utils/Bonjour.cpp
Normal file
704
xs/src/slic3r/Utils/Bonjour.cpp
Normal file
|
@ -0,0 +1,704 @@
|
||||||
|
#include "Bonjour.hpp"
|
||||||
|
|
||||||
|
#include <iostream> // XXX
|
||||||
|
#include <cstdint>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <random>
|
||||||
|
#include <thread>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
#include <boost/endian/conversion.hpp>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/date_time/posix_time/posix_time_duration.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
|
using boost::optional;
|
||||||
|
using boost::system::error_code;
|
||||||
|
namespace endian = boost::endian;
|
||||||
|
namespace asio = boost::asio;
|
||||||
|
using boost::asio::ip::udp;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Fuzzing test (done without TXT)
|
||||||
|
// FIXME: check char retype to unsigned
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
// Minimal implementation of a MDNS/DNS-SD client
|
||||||
|
// This implementation is extremely simple, only the bits that are useful
|
||||||
|
// for very basic MDNS discovery are present.
|
||||||
|
|
||||||
|
struct DnsName: public std::string
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
MAX_RECURSION = 10, // Keep this low
|
||||||
|
};
|
||||||
|
|
||||||
|
static optional<DnsName> decode(const std::vector<char> &buffer, size_t &offset, unsigned depth = 0)
|
||||||
|
{
|
||||||
|
// Check offset sanity:
|
||||||
|
if (offset + 1 >= buffer.size()) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for recursion depth to prevent parsing names that are nested too deeply
|
||||||
|
// or end up cyclic:
|
||||||
|
if (depth >= MAX_RECURSION) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
DnsName res;
|
||||||
|
const size_t bsize = buffer.size();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const char* ptr = buffer.data() + offset;
|
||||||
|
unsigned len = static_cast<unsigned char>(*ptr);
|
||||||
|
if (len & 0xc0) {
|
||||||
|
// This is a recursive label
|
||||||
|
unsigned len_2 = static_cast<unsigned char>(ptr[1]);
|
||||||
|
size_t pointer = (len & 0x3f) << 8 | len_2;
|
||||||
|
const auto nested = decode(buffer, pointer, depth + 1);
|
||||||
|
if (!nested) {
|
||||||
|
return boost::none;
|
||||||
|
} else {
|
||||||
|
if (res.size() > 0) {
|
||||||
|
res.push_back('.');
|
||||||
|
}
|
||||||
|
res.append(*nested);
|
||||||
|
offset += 2;
|
||||||
|
return std::move(res);
|
||||||
|
}
|
||||||
|
} else if (len == 0) {
|
||||||
|
// This is a name terminator
|
||||||
|
offset++;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// This is a regular label
|
||||||
|
len &= 0x3f;
|
||||||
|
if (len + offset + 1 >= bsize) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.reserve(len);
|
||||||
|
if (res.size() > 0) {
|
||||||
|
res.push_back('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr++;
|
||||||
|
for (const auto end = ptr + len; ptr < end; ptr++) {
|
||||||
|
char c = *ptr;
|
||||||
|
if (c >= 0x20 && c <= 0x7f) {
|
||||||
|
res.push_back(c);
|
||||||
|
} else {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += len + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.size() > 0) {
|
||||||
|
return std::move(res);
|
||||||
|
} else {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DnsHeader
|
||||||
|
{
|
||||||
|
uint16_t id;
|
||||||
|
uint16_t flags;
|
||||||
|
uint16_t qdcount;
|
||||||
|
uint16_t ancount;
|
||||||
|
uint16_t nscount;
|
||||||
|
uint16_t arcount;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SIZE = 12,
|
||||||
|
};
|
||||||
|
|
||||||
|
static DnsHeader decode(const std::vector<char> &buffer) {
|
||||||
|
DnsHeader res;
|
||||||
|
const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(buffer.data());
|
||||||
|
res.id = endian::big_to_native(data_16[0]);
|
||||||
|
res.flags = endian::big_to_native(data_16[1]);
|
||||||
|
res.qdcount = endian::big_to_native(data_16[2]);
|
||||||
|
res.ancount = endian::big_to_native(data_16[3]);
|
||||||
|
res.nscount = endian::big_to_native(data_16[4]);
|
||||||
|
res.arcount = endian::big_to_native(data_16[5]);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t rrcount() const {
|
||||||
|
return ancount + nscount + arcount;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DnsQuestion
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
MIN_SIZE = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
DnsName name;
|
||||||
|
uint16_t type;
|
||||||
|
uint16_t qclass;
|
||||||
|
|
||||||
|
DnsQuestion() :
|
||||||
|
type(0),
|
||||||
|
qclass(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
static optional<DnsQuestion> decode(const std::vector<char> &buffer, size_t &offset)
|
||||||
|
{
|
||||||
|
auto qname = DnsName::decode(buffer, offset);
|
||||||
|
if (!qname) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
DnsQuestion res;
|
||||||
|
res.name = std::move(*qname);
|
||||||
|
const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(buffer.data() + offset);
|
||||||
|
res.type = endian::big_to_native(data_16[0]);
|
||||||
|
res.qclass = endian::big_to_native(data_16[1]);
|
||||||
|
|
||||||
|
offset += 4;
|
||||||
|
return std::move(res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DnsResource
|
||||||
|
{
|
||||||
|
DnsName name;
|
||||||
|
uint16_t type;
|
||||||
|
uint16_t rclass;
|
||||||
|
uint32_t ttl;
|
||||||
|
std::vector<char> data;
|
||||||
|
|
||||||
|
DnsResource() :
|
||||||
|
type(0),
|
||||||
|
rclass(0),
|
||||||
|
ttl(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
static optional<DnsResource> decode(const std::vector<char> &buffer, size_t &offset, size_t &dataoffset)
|
||||||
|
{
|
||||||
|
const size_t bsize = buffer.size();
|
||||||
|
if (offset + 1 >= bsize) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rname = DnsName::decode(buffer, offset);
|
||||||
|
if (!rname) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset + 10 >= bsize) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
DnsResource res;
|
||||||
|
res.name = std::move(*rname);
|
||||||
|
const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(buffer.data() + offset);
|
||||||
|
res.type = endian::big_to_native(data_16[0]);
|
||||||
|
res.rclass = endian::big_to_native(data_16[1]);
|
||||||
|
res.ttl = endian::big_to_native(*reinterpret_cast<const uint32_t*>(data_16 + 2));
|
||||||
|
uint16_t rdlength = endian::big_to_native(data_16[4]);
|
||||||
|
|
||||||
|
offset += 10;
|
||||||
|
if (offset + rdlength > bsize) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataoffset = offset;
|
||||||
|
res.data = std::move(std::vector<char>(buffer.begin() + offset, buffer.begin() + offset + rdlength));
|
||||||
|
offset += rdlength;
|
||||||
|
|
||||||
|
return std::move(res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DnsRR_A
|
||||||
|
{
|
||||||
|
enum { TAG = 0x1 };
|
||||||
|
|
||||||
|
asio::ip::address_v4 ip;
|
||||||
|
|
||||||
|
static void decode(optional<DnsRR_A> &result, const DnsResource &rr)
|
||||||
|
{
|
||||||
|
if (rr.data.size() == 4) {
|
||||||
|
DnsRR_A res;
|
||||||
|
const uint32_t ip = endian::big_to_native(*reinterpret_cast<const uint32_t*>(rr.data.data()));
|
||||||
|
res.ip = asio::ip::address_v4(ip);
|
||||||
|
result = std::move(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DnsRR_AAAA
|
||||||
|
{
|
||||||
|
enum { TAG = 0x1c };
|
||||||
|
|
||||||
|
asio::ip::address_v6 ip;
|
||||||
|
|
||||||
|
static void decode(optional<DnsRR_AAAA> &result, const DnsResource &rr)
|
||||||
|
{
|
||||||
|
if (rr.data.size() == 16) {
|
||||||
|
DnsRR_AAAA res;
|
||||||
|
std::array<unsigned char, 16> ip;
|
||||||
|
std::copy_n(rr.data.begin(), 16, ip.begin());
|
||||||
|
res.ip = asio::ip::address_v6(ip);
|
||||||
|
result = std::move(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DnsRR_SRV
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TAG = 0x21,
|
||||||
|
MIN_SIZE = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint16_t priority;
|
||||||
|
uint16_t weight;
|
||||||
|
uint16_t port;
|
||||||
|
DnsName hostname;
|
||||||
|
|
||||||
|
static optional<DnsRR_SRV> decode(const std::vector<char> &buffer, const DnsResource &rr, size_t dataoffset)
|
||||||
|
{
|
||||||
|
if (rr.data.size() < MIN_SIZE) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
DnsRR_SRV res;
|
||||||
|
|
||||||
|
const uint16_t *data_16 = reinterpret_cast<const uint16_t*>(rr.data.data());
|
||||||
|
res.priority = endian::big_to_native(data_16[0]);
|
||||||
|
res.weight = endian::big_to_native(data_16[1]);
|
||||||
|
res.port = endian::big_to_native(data_16[2]);
|
||||||
|
|
||||||
|
size_t offset = dataoffset + 6;
|
||||||
|
auto hostname = DnsName::decode(buffer, offset);
|
||||||
|
|
||||||
|
if (hostname) {
|
||||||
|
res.hostname = std::move(*hostname);
|
||||||
|
return std::move(res);
|
||||||
|
} else {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DnsRR_TXT
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
TAG = 0x10,
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::string> values;
|
||||||
|
|
||||||
|
static optional<DnsRR_TXT> decode(const DnsResource &rr)
|
||||||
|
{
|
||||||
|
const size_t size = rr.data.size();
|
||||||
|
if (size < 2) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
DnsRR_TXT res;
|
||||||
|
|
||||||
|
for (auto it = rr.data.begin(); it != rr.data.end(); ) {
|
||||||
|
unsigned val_size = static_cast<unsigned char>(*it);
|
||||||
|
if (val_size == 0 || it + val_size >= rr.data.end()) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
|
||||||
|
std::string value(val_size, ' ');
|
||||||
|
std::copy(it, it + val_size, value.begin());
|
||||||
|
res.values.push_back(std::move(value));
|
||||||
|
|
||||||
|
it += val_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DnsSDPair
|
||||||
|
{
|
||||||
|
optional<DnsRR_SRV> srv;
|
||||||
|
optional<DnsRR_TXT> txt;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DnsSDMap : public std::map<std::string, DnsSDPair>
|
||||||
|
{
|
||||||
|
void insert_srv(std::string &&name, DnsRR_SRV &&srv)
|
||||||
|
{
|
||||||
|
auto hit = this->find(name);
|
||||||
|
if (hit != this->end()) {
|
||||||
|
hit->second.srv = std::move(srv);
|
||||||
|
} else {
|
||||||
|
DnsSDPair pair;
|
||||||
|
pair.srv = std::move(srv);
|
||||||
|
this->insert(std::make_pair(std::move(name), std::move(pair)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert_txt(std::string &&name, DnsRR_TXT &&txt)
|
||||||
|
{
|
||||||
|
auto hit = this->find(name);
|
||||||
|
if (hit != this->end()) {
|
||||||
|
hit->second.txt = std::move(txt);
|
||||||
|
} else {
|
||||||
|
DnsSDPair pair;
|
||||||
|
pair.txt = std::move(txt);
|
||||||
|
this->insert(std::make_pair(std::move(name), std::move(pair)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DnsMessage
|
||||||
|
{
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
MAX_SIZE = 4096,
|
||||||
|
MAX_ANS = 30,
|
||||||
|
};
|
||||||
|
|
||||||
|
DnsHeader header;
|
||||||
|
optional<DnsQuestion> question;
|
||||||
|
|
||||||
|
optional<DnsRR_A> rr_a;
|
||||||
|
optional<DnsRR_AAAA> rr_aaaa;
|
||||||
|
std::vector<DnsRR_SRV> rr_srv;
|
||||||
|
|
||||||
|
DnsSDMap sdmap;
|
||||||
|
|
||||||
|
static optional<DnsMessage> decode(const std::vector<char> &buffer, optional<uint16_t> id_wanted = boost::none)
|
||||||
|
{
|
||||||
|
const auto size = buffer.size();
|
||||||
|
if (size < DnsHeader::SIZE + DnsQuestion::MIN_SIZE || size > MAX_SIZE) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
DnsMessage res;
|
||||||
|
res.header = DnsHeader::decode(buffer);
|
||||||
|
|
||||||
|
if (id_wanted && *id_wanted != res.header.id) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.header.qdcount > 1 || res.header.ancount > MAX_ANS) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t offset = DnsHeader::SIZE;
|
||||||
|
if (res.header.qdcount == 1) {
|
||||||
|
res.question = DnsQuestion::decode(buffer, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < res.header.rrcount(); i++) {
|
||||||
|
size_t dataoffset = 0;
|
||||||
|
auto rr = DnsResource::decode(buffer, offset, dataoffset);
|
||||||
|
if (!rr) {
|
||||||
|
return boost::none;
|
||||||
|
} else {
|
||||||
|
res.parse_rr(buffer, std::move(*rr), dataoffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(res);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void parse_rr(const std::vector<char> &buffer, DnsResource &&rr, size_t dataoffset)
|
||||||
|
{
|
||||||
|
switch (rr.type) {
|
||||||
|
case DnsRR_A::TAG: DnsRR_A::decode(this->rr_a, rr); break;
|
||||||
|
case DnsRR_AAAA::TAG: DnsRR_AAAA::decode(this->rr_aaaa, rr); break;
|
||||||
|
case DnsRR_SRV::TAG: {
|
||||||
|
auto srv = DnsRR_SRV::decode(buffer, rr, dataoffset);
|
||||||
|
if (srv) { this->sdmap.insert_srv(std::move(rr.name), std::move(*srv)); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DnsRR_TXT::TAG: {
|
||||||
|
auto txt = DnsRR_TXT::decode(rr);
|
||||||
|
if (txt) { this->sdmap.insert_txt(std::move(rr.name), std::move(*txt)); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct BonjourRequest
|
||||||
|
{
|
||||||
|
static const asio::ip::address_v4 MCAST_IP4;
|
||||||
|
static const uint16_t MCAST_PORT;
|
||||||
|
|
||||||
|
uint16_t id;
|
||||||
|
std::vector<char> data;
|
||||||
|
|
||||||
|
static optional<BonjourRequest> make(const std::string &service, const std::string &protocol);
|
||||||
|
|
||||||
|
private:
|
||||||
|
BonjourRequest(uint16_t id, std::vector<char> &&data) :
|
||||||
|
id(id),
|
||||||
|
data(std::move(data))
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
const asio::ip::address_v4 BonjourRequest::MCAST_IP4{0xe00000fb};
|
||||||
|
const uint16_t BonjourRequest::MCAST_PORT = 5353;
|
||||||
|
|
||||||
|
optional<BonjourRequest> BonjourRequest::make(const std::string &service, const std::string &protocol)
|
||||||
|
{
|
||||||
|
if (service.size() > 15 || protocol.size() > 15) {
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::random_device dev;
|
||||||
|
std::uniform_int_distribution<uint16_t> dist;
|
||||||
|
uint16_t id = dist(dev);
|
||||||
|
uint16_t id_big = endian::native_to_big(id);
|
||||||
|
const char *id_char = reinterpret_cast<char*>(&id_big);
|
||||||
|
|
||||||
|
std::vector<char> data;
|
||||||
|
data.reserve(service.size() + 18);
|
||||||
|
|
||||||
|
// Add the transaction ID
|
||||||
|
data.push_back(id_char[0]);
|
||||||
|
data.push_back(id_char[1]);
|
||||||
|
|
||||||
|
// Add metadata
|
||||||
|
static const unsigned char rq_meta[] = {
|
||||||
|
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
std::copy(rq_meta, rq_meta + sizeof(rq_meta), std::back_inserter(data));
|
||||||
|
|
||||||
|
// Add PTR query name
|
||||||
|
data.push_back(service.size() + 1);
|
||||||
|
data.push_back('_');
|
||||||
|
data.insert(data.end(), service.begin(), service.end());
|
||||||
|
data.push_back(protocol.size() + 1);
|
||||||
|
data.push_back('_');
|
||||||
|
data.insert(data.end(), protocol.begin(), protocol.end());
|
||||||
|
|
||||||
|
// Add the rest of PTR record
|
||||||
|
static const unsigned char ptr_tail[] = {
|
||||||
|
0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x00, 0x00, 0x0c, 0x00, 0xff,
|
||||||
|
};
|
||||||
|
std::copy(ptr_tail, ptr_tail + sizeof(ptr_tail), std::back_inserter(data));
|
||||||
|
|
||||||
|
return BonjourRequest(id, std::move(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// API - private part
|
||||||
|
|
||||||
|
struct Bonjour::priv
|
||||||
|
{
|
||||||
|
const std::string service;
|
||||||
|
const std::string protocol;
|
||||||
|
const std::string service_dn;
|
||||||
|
unsigned timeout;
|
||||||
|
uint16_t rq_id;
|
||||||
|
|
||||||
|
std::vector<char> buffer;
|
||||||
|
std::thread io_thread;
|
||||||
|
Bonjour::ReplyFn replyfn;
|
||||||
|
Bonjour::CompleteFn completefn;
|
||||||
|
|
||||||
|
priv(std::string service, std::string protocol);
|
||||||
|
|
||||||
|
void udp_receive(udp::endpoint from, size_t bytes);
|
||||||
|
void lookup_perform();
|
||||||
|
};
|
||||||
|
|
||||||
|
Bonjour::priv::priv(std::string service, std::string protocol) :
|
||||||
|
service(std::move(service)),
|
||||||
|
protocol(std::move(protocol)),
|
||||||
|
service_dn((boost::format("_%1%._%2%.local") % this->service % this->protocol).str()),
|
||||||
|
timeout(10),
|
||||||
|
rq_id(0)
|
||||||
|
{
|
||||||
|
buffer.resize(DnsMessage::MAX_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bonjour::priv::udp_receive(udp::endpoint from, size_t bytes)
|
||||||
|
{
|
||||||
|
if (bytes == 0 || !replyfn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.resize(bytes);
|
||||||
|
const auto dns_msg = DnsMessage::decode(buffer, rq_id);
|
||||||
|
if (dns_msg) {
|
||||||
|
asio::ip::address ip = from.address();
|
||||||
|
if (dns_msg->rr_a) { ip = dns_msg->rr_a->ip; }
|
||||||
|
else if (dns_msg->rr_aaaa) { ip = dns_msg->rr_aaaa->ip; }
|
||||||
|
|
||||||
|
for (const auto &sdpair : dns_msg->sdmap) {
|
||||||
|
if (! sdpair.second.srv) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &srv = *sdpair.second.srv;
|
||||||
|
BonjourReply reply(ip, sdpair.first, srv.hostname);
|
||||||
|
|
||||||
|
if (sdpair.second.txt) {
|
||||||
|
static const std::string tag_path = "path=";
|
||||||
|
static const std::string tag_version = "version=";
|
||||||
|
|
||||||
|
for (const auto &value : sdpair.second.txt->values) {
|
||||||
|
if (value.size() > tag_path.size() && value.compare(0, tag_path.size(), tag_path) == 0) {
|
||||||
|
reply.path = value.substr(tag_path.size());
|
||||||
|
} else if (value.size() > tag_version.size() && value.compare(0, tag_version.size(), tag_version) == 0) {
|
||||||
|
reply.version = value.substr(tag_version.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
replyfn(std::move(reply));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bonjour::priv::lookup_perform()
|
||||||
|
{
|
||||||
|
const auto brq = BonjourRequest::make(service, protocol);
|
||||||
|
if (!brq) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto self = this;
|
||||||
|
rq_id = brq->id;
|
||||||
|
|
||||||
|
try {
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
udp::socket socket(io_service);
|
||||||
|
socket.open(udp::v4());
|
||||||
|
socket.set_option(udp::socket::reuse_address(true));
|
||||||
|
udp::endpoint mcast(BonjourRequest::MCAST_IP4, BonjourRequest::MCAST_PORT);
|
||||||
|
socket.send_to(asio::buffer(brq->data), mcast);
|
||||||
|
|
||||||
|
bool timeout = false;
|
||||||
|
asio::deadline_timer timer(io_service);
|
||||||
|
timer.expires_from_now(boost::posix_time::seconds(10));
|
||||||
|
timer.async_wait([=, &timeout](const error_code &error) {
|
||||||
|
timeout = true;
|
||||||
|
if (self->completefn) {
|
||||||
|
self->completefn();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
udp::endpoint recv_from;
|
||||||
|
const auto recv_handler = [&](const error_code &error, size_t bytes) {
|
||||||
|
if (!error) { self->udp_receive(recv_from, bytes); }
|
||||||
|
};
|
||||||
|
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
|
||||||
|
|
||||||
|
while (io_service.run_one()) {
|
||||||
|
if (timeout) {
|
||||||
|
socket.cancel();
|
||||||
|
} else {
|
||||||
|
buffer.resize(DnsMessage::MAX_SIZE);
|
||||||
|
socket.async_receive_from(asio::buffer(buffer, buffer.size()), recv_from, recv_handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// API - public part
|
||||||
|
|
||||||
|
BonjourReply::BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname) :
|
||||||
|
ip(std::move(ip)),
|
||||||
|
service_name(std::move(service_name)),
|
||||||
|
hostname(std::move(hostname)),
|
||||||
|
path("/"),
|
||||||
|
version("Unknown")
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream &os, const BonjourReply &reply)
|
||||||
|
{
|
||||||
|
os << "BonjourReply(" << reply.ip.to_string() << ", " << reply.service_name << ", "
|
||||||
|
<< reply.hostname << ", " << reply.path << ", " << reply.version << ")";
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bonjour::Bonjour(std::string service, std::string protocol) :
|
||||||
|
p(new priv(std::move(service), std::move(protocol)))
|
||||||
|
{}
|
||||||
|
|
||||||
|
Bonjour::Bonjour(Bonjour &&other) : p(std::move(other.p)) {}
|
||||||
|
|
||||||
|
Bonjour::~Bonjour()
|
||||||
|
{
|
||||||
|
if (p && p->io_thread.joinable()) {
|
||||||
|
p->io_thread.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bonjour& Bonjour::set_timeout(unsigned timeout)
|
||||||
|
{
|
||||||
|
if (p) { p->timeout = timeout; }
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bonjour& Bonjour::on_reply(ReplyFn fn)
|
||||||
|
{
|
||||||
|
if (p) { p->replyfn = std::move(fn); }
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bonjour& Bonjour::on_complete(CompleteFn fn)
|
||||||
|
{
|
||||||
|
if (p) { p->completefn = std::move(fn); }
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bonjour::Ptr Bonjour::lookup()
|
||||||
|
{
|
||||||
|
auto self = std::make_shared<Bonjour>(std::move(*this));
|
||||||
|
|
||||||
|
if (self->p) {
|
||||||
|
auto io_thread = std::thread([self](){
|
||||||
|
self->p->lookup_perform();
|
||||||
|
});
|
||||||
|
self->p->io_thread = std::move(io_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Bonjour::pokus() // XXX
|
||||||
|
{
|
||||||
|
auto bonjour = Bonjour("octoprint")
|
||||||
|
.set_timeout(15)
|
||||||
|
.on_reply([](BonjourReply &&reply) {
|
||||||
|
std::cerr << "BonjourReply: " << reply << std::endl;
|
||||||
|
})
|
||||||
|
.on_complete([](){
|
||||||
|
std::cerr << "MDNS lookup complete" << std::endl;
|
||||||
|
})
|
||||||
|
.lookup();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
56
xs/src/slic3r/Utils/Bonjour.hpp
Normal file
56
xs/src/slic3r/Utils/Bonjour.hpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef slic3r_Bonjour_hpp_
|
||||||
|
#define slic3r_Bonjour_hpp_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
// #include <ostream>
|
||||||
|
#include <boost/asio/ip/address.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: reply data structure
|
||||||
|
struct BonjourReply
|
||||||
|
{
|
||||||
|
boost::asio::ip::address ip;
|
||||||
|
std::string service_name;
|
||||||
|
std::string hostname;
|
||||||
|
std::string path;
|
||||||
|
std::string version;
|
||||||
|
|
||||||
|
BonjourReply(boost::asio::ip::address ip, std::string service_name, std::string hostname);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream &, const BonjourReply &);
|
||||||
|
|
||||||
|
|
||||||
|
/// Bonjour lookup performer
|
||||||
|
class Bonjour : public std::enable_shared_from_this<Bonjour> {
|
||||||
|
private:
|
||||||
|
struct priv;
|
||||||
|
public:
|
||||||
|
typedef std::shared_ptr<Bonjour> Ptr;
|
||||||
|
typedef std::function<void(BonjourReply &&reply)> ReplyFn;
|
||||||
|
typedef std::function<void()> CompleteFn;
|
||||||
|
|
||||||
|
Bonjour(std::string service, std::string protocol = "tcp");
|
||||||
|
Bonjour(Bonjour &&other);
|
||||||
|
~Bonjour();
|
||||||
|
|
||||||
|
Bonjour& set_timeout(unsigned timeout);
|
||||||
|
Bonjour& on_reply(ReplyFn fn);
|
||||||
|
Bonjour& on_complete(CompleteFn fn);
|
||||||
|
|
||||||
|
Ptr lookup();
|
||||||
|
|
||||||
|
static void pokus(); // XXX: remove
|
||||||
|
private:
|
||||||
|
std::unique_ptr<priv> p;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
261
xs/src/slic3r/Utils/Http.cpp
Normal file
261
xs/src/slic3r/Utils/Http.cpp
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
#include "Http.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
#include <iostream>
|
||||||
|
#include <tuple>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include "../../libslic3r/libslic3r.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
// Private
|
||||||
|
|
||||||
|
class CurlGlobalInit
|
||||||
|
{
|
||||||
|
static const CurlGlobalInit instance;
|
||||||
|
|
||||||
|
CurlGlobalInit() { ::curl_global_init(CURL_GLOBAL_DEFAULT); }
|
||||||
|
~CurlGlobalInit() { ::curl_global_cleanup(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Http::priv
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
DEFAULT_SIZE_LIMIT = 5 * 1024 * 1024,
|
||||||
|
};
|
||||||
|
|
||||||
|
::CURL *curl;
|
||||||
|
::curl_httppost *form;
|
||||||
|
::curl_httppost *form_end;
|
||||||
|
::curl_slist *headerlist;
|
||||||
|
std::string buffer;
|
||||||
|
size_t limit;
|
||||||
|
|
||||||
|
std::thread io_thread;
|
||||||
|
Http::CompleteFn completefn;
|
||||||
|
Http::ErrorFn errorfn;
|
||||||
|
|
||||||
|
priv(const std::string &url);
|
||||||
|
~priv();
|
||||||
|
|
||||||
|
static size_t writecb(void *data, size_t size, size_t nmemb, void *userp);
|
||||||
|
std::string body_size_error();
|
||||||
|
void http_perform();
|
||||||
|
};
|
||||||
|
|
||||||
|
Http::priv::priv(const std::string &url) :
|
||||||
|
curl(::curl_easy_init()),
|
||||||
|
form(nullptr),
|
||||||
|
form_end(nullptr),
|
||||||
|
headerlist(nullptr)
|
||||||
|
{
|
||||||
|
if (curl == nullptr) {
|
||||||
|
throw std::runtime_error(std::string("Could not construct Curl object"));
|
||||||
|
}
|
||||||
|
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
Http::priv::~priv()
|
||||||
|
{
|
||||||
|
::curl_easy_cleanup(curl);
|
||||||
|
::curl_formfree(form);
|
||||||
|
::curl_slist_free_all(headerlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
|
||||||
|
{
|
||||||
|
auto self = static_cast<priv*>(userp);
|
||||||
|
const char *cdata = static_cast<char*>(data);
|
||||||
|
const size_t realsize = size * nmemb;
|
||||||
|
|
||||||
|
const size_t limit = self->limit > 0 ? self->limit : DEFAULT_SIZE_LIMIT;
|
||||||
|
if (self->buffer.size() + realsize > limit) {
|
||||||
|
// This makes curl_easy_perform return CURLE_WRITE_ERROR
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->buffer.append(cdata, realsize);
|
||||||
|
|
||||||
|
return realsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Http::priv::body_size_error()
|
||||||
|
{
|
||||||
|
return (boost::format("HTTP body data size exceeded limit (%1% bytes)") % limit).str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Http::priv::http_perform()
|
||||||
|
{
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb);
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(this));
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (headerlist != nullptr) {
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (form != nullptr) {
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_HTTPPOST, form);
|
||||||
|
}
|
||||||
|
|
||||||
|
CURLcode res = ::curl_easy_perform(curl);
|
||||||
|
long http_status = 0;
|
||||||
|
::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status);
|
||||||
|
|
||||||
|
if (res != CURLE_OK) {
|
||||||
|
std::string error;
|
||||||
|
if (res == CURLE_WRITE_ERROR) {
|
||||||
|
error = std::move(body_size_error());
|
||||||
|
} else {
|
||||||
|
error = ::curl_easy_strerror(res);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (errorfn) {
|
||||||
|
errorfn(std::move(buffer), std::move(error), http_status);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (completefn) {
|
||||||
|
completefn(std::move(buffer), http_status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Http::Http(const std::string &url) : p(new priv(url)) {}
|
||||||
|
|
||||||
|
|
||||||
|
// Public
|
||||||
|
|
||||||
|
Http::Http(Http &&other) : p(std::move(other.p)) {}
|
||||||
|
|
||||||
|
Http::~Http()
|
||||||
|
{
|
||||||
|
if (p && p->io_thread.joinable()) {
|
||||||
|
p->io_thread.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Http& Http::size_limit(size_t sizeLimit)
|
||||||
|
{
|
||||||
|
if (p) { p->limit = sizeLimit; }
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::header(std::string name, const std::string &value)
|
||||||
|
{
|
||||||
|
if (!p) { return * this; }
|
||||||
|
|
||||||
|
if (name.size() > 0) {
|
||||||
|
name.append(": ").append(value);
|
||||||
|
} else {
|
||||||
|
name.push_back(':');
|
||||||
|
}
|
||||||
|
p->headerlist = curl_slist_append(p->headerlist, name.c_str());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::remove_header(std::string name)
|
||||||
|
{
|
||||||
|
if (p) {
|
||||||
|
name.push_back(':');
|
||||||
|
p->headerlist = curl_slist_append(p->headerlist, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::ca_file(const std::string &name)
|
||||||
|
{
|
||||||
|
if (p) {
|
||||||
|
::curl_easy_setopt(p->curl, CURLOPT_CAINFO, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::form_add(const std::string &name, const std::string &contents)
|
||||||
|
{
|
||||||
|
if (p) {
|
||||||
|
::curl_formadd(&p->form, &p->form_end,
|
||||||
|
CURLFORM_COPYNAME, name.c_str(),
|
||||||
|
CURLFORM_COPYCONTENTS, contents.c_str(),
|
||||||
|
CURLFORM_END
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::form_add_file(const std::string &name, const std::string &filename)
|
||||||
|
{
|
||||||
|
if (p) {
|
||||||
|
::curl_formadd(&p->form, &p->form_end,
|
||||||
|
CURLFORM_COPYNAME, name.c_str(),
|
||||||
|
CURLFORM_FILE, filename.c_str(),
|
||||||
|
CURLFORM_CONTENTTYPE, "application/octet-stream",
|
||||||
|
CURLFORM_END
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::on_complete(CompleteFn fn)
|
||||||
|
{
|
||||||
|
if (p) { p->completefn = std::move(fn); }
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::on_error(ErrorFn fn)
|
||||||
|
{
|
||||||
|
if (p) { p->errorfn = std::move(fn); }
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http::Ptr Http::perform()
|
||||||
|
{
|
||||||
|
auto self = std::make_shared<Http>(std::move(*this));
|
||||||
|
|
||||||
|
if (self->p) {
|
||||||
|
auto io_thread = std::thread([self](){
|
||||||
|
self->p->http_perform();
|
||||||
|
});
|
||||||
|
self->p->io_thread = std::move(io_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Http::perform_sync()
|
||||||
|
{
|
||||||
|
if (p) { p->http_perform(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
Http Http::get(std::string url)
|
||||||
|
{
|
||||||
|
return std::move(Http{std::move(url)});
|
||||||
|
}
|
||||||
|
|
||||||
|
Http Http::post(std::string url)
|
||||||
|
{
|
||||||
|
Http http{std::move(url)};
|
||||||
|
curl_easy_setopt(http.p->curl, CURLOPT_POST, 1L);
|
||||||
|
return http;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
53
xs/src/slic3r/Utils/Http.hpp
Normal file
53
xs/src/slic3r/Utils/Http.hpp
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef slic3r_Http_hpp_
|
||||||
|
#define slic3r_Http_hpp_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
/// Represetns a Http request
|
||||||
|
class Http : public std::enable_shared_from_this<Http> {
|
||||||
|
private:
|
||||||
|
struct priv;
|
||||||
|
public:
|
||||||
|
typedef std::shared_ptr<Http> Ptr;
|
||||||
|
typedef std::function<void(std::string /* body */, unsigned /* http_status */)> CompleteFn;
|
||||||
|
typedef std::function<void(std::string /* body */, std::string /* error */, unsigned /* http_status */)> ErrorFn;
|
||||||
|
|
||||||
|
Http(Http &&other);
|
||||||
|
|
||||||
|
static Http get(std::string url);
|
||||||
|
static Http post(std::string url);
|
||||||
|
~Http();
|
||||||
|
|
||||||
|
Http(const Http &) = delete;
|
||||||
|
Http& operator=(const Http &) = delete;
|
||||||
|
Http& operator=(Http &&) = delete;
|
||||||
|
|
||||||
|
Http& size_limit(size_t sizeLimit);
|
||||||
|
Http& header(std::string name, const std::string &value);
|
||||||
|
Http& remove_header(std::string name);
|
||||||
|
Http& ca_file(const std::string &filename);
|
||||||
|
Http& form_add(const std::string &name, const std::string &contents);
|
||||||
|
Http& form_add_file(const std::string &name, const std::string &filename);
|
||||||
|
|
||||||
|
Http& on_complete(CompleteFn fn);
|
||||||
|
Http& on_error(ErrorFn fn);
|
||||||
|
|
||||||
|
Ptr perform();
|
||||||
|
void perform_sync();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Http(const std::string &url);
|
||||||
|
|
||||||
|
std::unique_ptr<priv> p;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
105
xs/src/slic3r/Utils/OctoPrint.cpp
Normal file
105
xs/src/slic3r/Utils/OctoPrint.cpp
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#include "OctoPrint.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
|
#include <wx/frame.h>
|
||||||
|
#include <wx/event.h>
|
||||||
|
|
||||||
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
|
#include "Http.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
OctoPrint::OctoPrint(DynamicPrintConfig *config) :
|
||||||
|
host(config->opt_string("octoprint_host")),
|
||||||
|
apikey(config->opt_string("octoprint_apikey")),
|
||||||
|
cafile(config->opt_string("octoprint_cafile"))
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string OctoPrint::test() const
|
||||||
|
{
|
||||||
|
// Since the request is performed synchronously here,
|
||||||
|
// it is ok to refer to `res` from within the closure
|
||||||
|
std::string res;
|
||||||
|
|
||||||
|
auto http = Http::get(std::move(make_url("api/version")));
|
||||||
|
set_auth(http);
|
||||||
|
http.on_error([&](std::string, std::string error, unsigned status) {
|
||||||
|
res = format_error(error, status);
|
||||||
|
})
|
||||||
|
.perform_sync();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctoPrint::send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print) const
|
||||||
|
{
|
||||||
|
auto http = Http::post(std::move(make_url("api/files/local")));
|
||||||
|
set_auth(http);
|
||||||
|
http.form_add("print", print ? "true" : "false")
|
||||||
|
.form_add_file("file", filename)
|
||||||
|
.on_complete([=](std::string body, unsigned status) {
|
||||||
|
wxWindow *window = GUI::get_widget_by_id(windowId);
|
||||||
|
wxCommandEvent* evt = new wxCommandEvent(completeEvt);
|
||||||
|
evt->SetString("G-code file successfully uploaded to the OctoPrint server");
|
||||||
|
evt->SetInt(100);
|
||||||
|
wxQueueEvent(window, evt);
|
||||||
|
})
|
||||||
|
.on_error([=](std::string body, std::string error, unsigned status) {
|
||||||
|
wxWindow *window = GUI::get_widget_by_id(windowId);
|
||||||
|
|
||||||
|
wxCommandEvent* evt_complete = new wxCommandEvent(completeEvt);
|
||||||
|
evt_complete->SetInt(100);
|
||||||
|
wxQueueEvent(window, evt_complete);
|
||||||
|
|
||||||
|
wxCommandEvent* evt_error = new wxCommandEvent(errorEvt);
|
||||||
|
evt_error->SetString(wxString::Format("Error while uploading to the OctoPrint server: %s", format_error(error, status)));
|
||||||
|
wxQueueEvent(window, evt_error);
|
||||||
|
})
|
||||||
|
.perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OctoPrint::set_auth(Http &http) const
|
||||||
|
{
|
||||||
|
http.header("X-Api-Key", apikey);
|
||||||
|
|
||||||
|
if (! cafile.empty()) {
|
||||||
|
http.ca_file(cafile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OctoPrint::make_url(const std::string &path) const
|
||||||
|
{
|
||||||
|
if (host.find("http://") == 0 || host.find("https://") == 0) {
|
||||||
|
if (host.back() == '/') {
|
||||||
|
return std::move((boost::format("%1%%2%") % host % path).str());
|
||||||
|
} else {
|
||||||
|
return std::move((boost::format("%1%/%2%") % host % path).str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return std::move((boost::format("http://%1%/%2%") % host % path).str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string OctoPrint::format_error(std::string error, unsigned status)
|
||||||
|
{
|
||||||
|
if (status != 0) {
|
||||||
|
std::string res{"HTTP "};
|
||||||
|
res.append(std::to_string(status));
|
||||||
|
|
||||||
|
if (status == 401) {
|
||||||
|
res.append(": Invalid API key");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(res);
|
||||||
|
} else {
|
||||||
|
return std::move(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
35
xs/src/slic3r/Utils/OctoPrint.hpp
Normal file
35
xs/src/slic3r/Utils/OctoPrint.hpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef slic3r_OctoPrint_hpp_
|
||||||
|
#define slic3r_OctoPrint_hpp_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// #include "Http.hpp" // XXX: ?
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicPrintConfig;
|
||||||
|
class Http;
|
||||||
|
|
||||||
|
class OctoPrint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OctoPrint(DynamicPrintConfig *config);
|
||||||
|
|
||||||
|
std::string test() const;
|
||||||
|
// XXX: style
|
||||||
|
void send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print = false) const;
|
||||||
|
private:
|
||||||
|
std::string host;
|
||||||
|
std::string apikey;
|
||||||
|
std::string cafile;
|
||||||
|
|
||||||
|
void set_auth(Http &http) const;
|
||||||
|
std::string make_url(const std::string &path) const;
|
||||||
|
static std::string format_error(std::string error, unsigned status);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -59,6 +59,15 @@ extern "C" {
|
||||||
#undef seek
|
#undef seek
|
||||||
#undef send
|
#undef send
|
||||||
#undef write
|
#undef write
|
||||||
|
#undef open
|
||||||
|
#undef close
|
||||||
|
#undef seekdir
|
||||||
|
#undef setbuf
|
||||||
|
#undef fread
|
||||||
|
#undef fseek
|
||||||
|
#undef fputc
|
||||||
|
#undef fwrite
|
||||||
|
#undef fclose
|
||||||
#endif /* _MSC_VER */
|
#endif /* _MSC_VER */
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -35,11 +35,11 @@ void set_tab_panel(SV *ui)
|
||||||
void add_debug_menu(SV *ui, int event_language_change)
|
void add_debug_menu(SV *ui, int event_language_change)
|
||||||
%code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_language_change); %};
|
%code%{ Slic3r::GUI::add_debug_menu((wxMenuBar*)wxPli_sv_2_object(aTHX_ ui, "Wx::MenuBar"), event_language_change); %};
|
||||||
|
|
||||||
void create_preset_tabs(PresetBundle *preset_bundle, AppConfig *app_config,
|
void create_preset_tabs(PresetBundle *preset_bundle, bool no_controller,
|
||||||
bool no_controller, bool is_disabled_button_browse, bool is_user_agent,
|
bool is_disabled_button_browse, bool is_user_agent,
|
||||||
int event_value_change, int event_presets_changed,
|
int event_value_change, int event_presets_changed,
|
||||||
int event_button_browse, int event_button_test)
|
int event_button_browse, int event_button_test)
|
||||||
%code%{ Slic3r::GUI::create_preset_tabs(preset_bundle, app_config, no_controller,
|
%code%{ Slic3r::GUI::create_preset_tabs(preset_bundle, no_controller,
|
||||||
is_disabled_button_browse, is_user_agent,
|
is_disabled_button_browse, is_user_agent,
|
||||||
event_value_change, event_presets_changed,
|
event_value_change, event_presets_changed,
|
||||||
event_button_browse, event_button_test); %};
|
event_button_browse, event_button_test); %};
|
||||||
|
@ -55,3 +55,9 @@ void create_combochecklist(SV *ui, std::string text, std::string items, bool ini
|
||||||
|
|
||||||
int combochecklist_get_flags(SV *ui)
|
int combochecklist_get_flags(SV *ui)
|
||||||
%code%{ RETVAL=Slic3r::GUI::combochecklist_get_flags((wxComboCtrl*)wxPli_sv_2_object(aTHX_ ui, "Wx::ComboCtrl")); %};
|
%code%{ RETVAL=Slic3r::GUI::combochecklist_get_flags((wxComboCtrl*)wxPli_sv_2_object(aTHX_ ui, "Wx::ComboCtrl")); %};
|
||||||
|
|
||||||
|
void set_app_config(AppConfig *app_config)
|
||||||
|
%code%{ Slic3r::GUI::set_app_config(app_config); %};
|
||||||
|
|
||||||
|
void open_preferences_dialog(int preferences_event)
|
||||||
|
%code%{ Slic3r::GUI::open_preferences_dialog(preferences_event); %};
|
||||||
|
|
|
@ -85,7 +85,11 @@
|
||||||
|
|
||||||
int count()
|
int count()
|
||||||
%code{% RETVAL = THIS->volumes.size(); %};
|
%code{% RETVAL = THIS->volumes.size(); %};
|
||||||
|
|
||||||
|
std::vector<double> get_current_print_zs()
|
||||||
|
%code{% RETVAL = THIS->get_current_print_zs(); %};
|
||||||
|
|
||||||
|
|
||||||
void set_range(double low, double high);
|
void set_range(double low, double high);
|
||||||
|
|
||||||
void render_VBOs() const;
|
void render_VBOs() const;
|
||||||
|
|
14
xs/xsp/Utils_OctoPrint.xsp
Normal file
14
xs/xsp/Utils_OctoPrint.xsp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
%module{Slic3r::XS};
|
||||||
|
|
||||||
|
%{
|
||||||
|
#include <xsinit.h>
|
||||||
|
#include "slic3r/Utils/OctoPrint.hpp"
|
||||||
|
%}
|
||||||
|
|
||||||
|
%name{Slic3r::OctoPrint} class OctoPrint {
|
||||||
|
OctoPrint(DynamicPrintConfig *config);
|
||||||
|
~OctoPrint();
|
||||||
|
|
||||||
|
std::string test() const;
|
||||||
|
void send_gcode(int windowId, int completeEvt, int errorEvt, std::string filename, bool print = false) const;
|
||||||
|
};
|
|
@ -236,6 +236,10 @@ Ref<PresetHints> O_OBJECT_SLIC3R_T
|
||||||
TabIface* O_OBJECT_SLIC3R
|
TabIface* O_OBJECT_SLIC3R
|
||||||
Ref<TabIface> O_OBJECT_SLIC3R_T
|
Ref<TabIface> O_OBJECT_SLIC3R_T
|
||||||
|
|
||||||
|
OctoPrint* O_OBJECT_SLIC3R
|
||||||
|
Ref<OctoPrint> O_OBJECT_SLIC3R_T
|
||||||
|
Clone<OctoPrint> O_OBJECT_SLIC3R_T
|
||||||
|
|
||||||
Axis T_UV
|
Axis T_UV
|
||||||
ExtrusionLoopRole T_UV
|
ExtrusionLoopRole T_UV
|
||||||
ExtrusionRole T_UV
|
ExtrusionRole T_UV
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue