Merge remote-tracking branch 'origin/master' into ys_update_settings

This commit is contained in:
YuSanka 2019-08-22 10:26:31 +02:00
commit 7ff68ad210
94 changed files with 2540 additions and 2639 deletions

View file

@ -13,13 +13,13 @@ if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
endif() endif()
if(DEFINED ENV{SLIC3R_STATIC}) if(DEFINED ENV{SLIC3R_STATIC})
set(SLIC3R_STATIC_INITIAL $ENV{SLIC3R_STATIC}) set(SLIC3R_STATIC_INITIAL $ENV{SLIC3R_STATIC})
else() else()
if (MSVC OR MINGW OR APPLE) if (MSVC OR MINGW OR APPLE)
set(SLIC3R_STATIC_INITIAL 1) set(SLIC3R_STATIC_INITIAL 1)
else() else()
set(SLIC3R_STATIC_INITIAL 0) set(SLIC3R_STATIC_INITIAL 0)
endif() endif()
endif() endif()
option(SLIC3R_STATIC "Compile PrusaSlicer with static libraries (Boost, TBB, glew)" ${SLIC3R_STATIC_INITIAL}) option(SLIC3R_STATIC "Compile PrusaSlicer with static libraries (Boost, TBB, glew)" ${SLIC3R_STATIC_INITIAL})
@ -52,9 +52,21 @@ if (SLIC3R_GUI)
add_definitions(-DSLIC3R_GUI) add_definitions(-DSLIC3R_GUI)
endif () endif ()
if (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(IS_CLANG_CL TRUE)
# clang-cl can interpret SYSTEM header paths if -imsvc is used
set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-imsvc")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall \
-Wno-old-style-cast -Wno-reserved-id-macro -Wno-c++98-compat-pedantic")
else ()
set(IS_CLANG_CL FALSE)
endif ()
if (MSVC) if (MSVC)
if (SLIC3R_MSVC_COMPILE_PARALLEL) if (SLIC3R_MSVC_COMPILE_PARALLEL AND NOT IS_CLANG_CL)
add_compile_options(/MP) add_compile_options(/MP)
endif () endif ()
# /bigobj (Increase Number of Sections in .Obj file) # /bigobj (Increase Number of Sections in .Obj file)
# error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater # error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater
@ -62,6 +74,10 @@ if (MSVC)
add_compile_options(-bigobj -Zm520 /Zi) add_compile_options(-bigobj -Zm520 /Zi)
endif () endif ()
if (MINGW)
add_compile_options(-Wa,-mbig-obj)
endif ()
# Display and check CMAKE_PREFIX_PATH # Display and check CMAKE_PREFIX_PATH
message(STATUS "SLIC3R_STATIC: ${SLIC3R_STATIC}") message(STATUS "SLIC3R_STATIC: ${SLIC3R_STATIC}")
if (NOT "${CMAKE_PREFIX_PATH}" STREQUAL "") if (NOT "${CMAKE_PREFIX_PATH}" STREQUAL "")
@ -101,17 +117,17 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory. # WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory.
# We pick it from environment if it is not defined in another way # We pick it from environment if it is not defined in another way
if(WIN32) if(WIN32)
if(NOT DEFINED WIN10SDK_PATH) if(NOT DEFINED WIN10SDK_PATH)
if(DEFINED ENV{WIN10SDK_PATH}) if(DEFINED ENV{WIN10SDK_PATH})
set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}") set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}")
endif() endif()
endif() endif()
if(DEFINED WIN10SDK_PATH AND NOT EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h") if(DEFINED WIN10SDK_PATH AND NOT EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h")
message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}") message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}")
message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found") message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found")
message("STL fixing by the Netfabb service will not be compiled") message("STL fixing by the Netfabb service will not be compiled")
unset(WIN10SDK_PATH) unset(WIN10SDK_PATH)
endif() endif()
if(WIN10SDK_PATH) if(WIN10SDK_PATH)
message("Building with Win10 Netfabb STL fixing service support") message("Building with Win10 Netfabb STL fixing service support")
add_definitions(-DHAS_WIN10SDK) add_definitions(-DHAS_WIN10SDK)
@ -148,8 +164,10 @@ if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals" ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals" )
endif() endif()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if (NOT MSVC AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall" ) if (NOT MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall" )
endif ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder" ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder" )
# On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error. # On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error.
@ -168,7 +186,6 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATC
add_compile_options(-Wno-unknown-pragmas) add_compile_options(-Wno-unknown-pragmas)
endif() endif()
if (SLIC3R_ASAN) if (SLIC3R_ASAN)
add_compile_options(-fsanitize=address -fno-omit-frame-pointer) add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
@ -196,9 +213,12 @@ include_directories(${LIBDIR_BIN}/platform)
include_directories(${LIBDIR}/clipper ${LIBDIR}/polypartition) include_directories(${LIBDIR}/clipper ${LIBDIR}/polypartition)
if(WIN32) if(WIN32)
# BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking. add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS) if(MSVC)
endif() # BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking.
add_definitions(-DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601 )
endif(MSVC)
endif(WIN32)
add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE -DWXINTL_NO_GETTEXT_MACRO) add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE -DWXINTL_NO_GETTEXT_MACRO)
@ -229,7 +249,7 @@ if(SLIC3R_STATIC)
# set(Boost_USE_STATIC_RUNTIME ON) # set(Boost_USE_STATIC_RUNTIME ON)
endif() endif()
#set(Boost_DEBUG ON) #set(Boost_DEBUG ON)
# set(Boost_COMPILER "-vc120") # set(Boost_COMPILER "-mgw81")
if(NOT WIN32) if(NOT WIN32)
# boost::process was introduced first in version 1.64.0 # boost::process was introduced first in version 1.64.0
set(MINIMUM_BOOST_VERSION "1.64.0") set(MINIMUM_BOOST_VERSION "1.64.0")
@ -251,7 +271,7 @@ endif()
if(TARGET Boost::system) if(TARGET Boost::system)
message(STATUS "Boost::boost exists") message(STATUS "Boost::boost exists")
target_link_libraries(boost_headeronly INTERFACE Boost::boost) target_link_libraries(boost_headeronly INTERFACE Boost::boost)
target_link_libraries(boost_libs INTERFACE target_link_libraries(boost_libs INTERFACE
boost_headeronly # includes the custom compile definitions as well boost_headeronly # includes the custom compile definitions as well
Boost::system Boost::system
Boost::filesystem Boost::filesystem

View file

@ -105,6 +105,9 @@ function(add_precompiled_header _target _input)
cmake_parse_arguments(_PCH "FORCEINCLUDE" "SOURCE_CXX;SOURCE_C" "" ${ARGN}) cmake_parse_arguments(_PCH "FORCEINCLUDE" "SOURCE_CXX;SOURCE_C" "" ${ARGN})
get_filename_component(_input_we ${_input} NAME_WE) get_filename_component(_input_we ${_input} NAME_WE)
get_filename_component(_input_full ${_input} ABSOLUTE)
file(TO_NATIVE_PATH "${_input_full}" _input_fullpath)
if(NOT _PCH_SOURCE_CXX) if(NOT _PCH_SOURCE_CXX)
set(_PCH_SOURCE_CXX "${_input_we}.cpp") set(_PCH_SOURCE_CXX "${_input_we}.cpp")
endif() endif()
@ -138,16 +141,16 @@ function(add_precompiled_header _target _input)
set_source_files_properties("${_source}" PROPERTIES OBJECT_OUTPUTS "${_pch_c_pch}") set_source_files_properties("${_source}" PROPERTIES OBJECT_OUTPUTS "${_pch_c_pch}")
else() else()
if(_source MATCHES \\.\(cpp|cxx|cc\)$) if(_source MATCHES \\.\(cpp|cxx|cc\)$)
set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_cxx_pch}\" \"/Yu${_input}\"") set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_cxx_pch}\" \"/Yu${_input_fullpath}\"")
set(_pch_source_cxx_needed TRUE) set(_pch_source_cxx_needed TRUE)
set_source_files_properties("${_source}" PROPERTIES OBJECT_DEPENDS "${_pch_cxx_pch}") set_source_files_properties("${_source}" PROPERTIES OBJECT_DEPENDS "${_pch_cxx_pch}")
else() else()
set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_c_pch}\" \"/Yu${_input}\"") set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_c_pch}\" \"/Yu${_input_fullpath}\"")
set(_pch_source_c_needed TRUE) set(_pch_source_c_needed TRUE)
set_source_files_properties("${_source}" PROPERTIES OBJECT_DEPENDS "${_pch_c_pch}") set_source_files_properties("${_source}" PROPERTIES OBJECT_DEPENDS "${_pch_c_pch}")
endif() endif()
if(_PCH_FORCEINCLUDE) if(_PCH_FORCEINCLUDE)
set(_pch_compile_flags "${_pch_compile_flags} /FI${_input}") set(_pch_compile_flags "${_pch_compile_flags} /FI${_input_fullpath}")
endif(_PCH_FORCEINCLUDE) endif(_PCH_FORCEINCLUDE)
endif() endif()

5
deps/CMakeLists.txt vendored
View file

@ -76,7 +76,10 @@ elseif (APPLE)
endif () endif ()
include("deps-macos.cmake") include("deps-macos.cmake")
else () elseif (MINGW)
message(STATUS "Building for MinGW...")
include("deps-mingw.cmake")
else()
include("deps-linux.cmake") include("deps-linux.cmake")
endif() endif()

76
deps/deps-mingw.cmake vendored Normal file
View file

@ -0,0 +1,76 @@
set(DEP_CMAKE_OPTS "-DCMAKE_POSITION_INDEPENDENT_CODE=ON")
set(DEP_BOOST_TOOLSET "gcc")
set(DEP_BITS 64)
find_package(Git REQUIRED)
# TODO make sure to build tbb with -flifetime-dse=1
include("deps-unix-common.cmake")
ExternalProject_Add(dep_boost
EXCLUDE_FROM_ALL 1
URL "https://dl.bintray.com/boostorg/release/1.70.0/source/boost_1_70_0.tar.gz"
URL_HASH SHA256=882b48708d211a5f48e60b0124cf5863c1534cd544ecd0664bb534a4b5d506e9
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND bootstrap.bat
BUILD_COMMAND b2.exe
-j "${NPROC}"
--with-system
--with-filesystem
--with-thread
--with-log
--with-locale
--with-regex
"--prefix=${DESTDIR}/usr/local"
"address-model=${DEPS_BITS}"
"toolset=${DEP_BOOST_TOOLSET}"
link=static
define=BOOST_USE_WINAPI_VERSION=0x0502
variant=release
threading=multi
boost.locale.icu=off
"${DEP_BOOST_DEBUG}" release install
INSTALL_COMMAND "" # b2 does that already
)
ExternalProject_Add(dep_libcurl
EXCLUDE_FROM_ALL 1
URL "https://curl.haxx.se/download/curl-7.58.0.tar.gz"
URL_HASH SHA256=cc245bf9a1a42a45df491501d97d5593392a03f7b4f07b952793518d97666115
CMAKE_ARGS
-DBUILD_SHARED_LIBS=OFF
-DBUILD_TESTING=OFF
-DCURL_STATICLIB=ON
-DCURL_STATIC_CRT=ON
-DENABLE_THREADED_RESOLVER=ON
-DCURL_DISABLE_FTP=ON
-DCURL_DISABLE_LDAP=ON
-DCURL_DISABLE_LDAPS=ON
-DCURL_DISABLE_TELNET=ON
-DCURL_DISABLE_DICT=ON
-DCURL_DISABLE_FILE=ON
-DCURL_DISABLE_TFTP=ON
-DCURL_DISABLE_RTSP=ON
-DCURL_DISABLE_POP3=ON
-DCURL_DISABLE_IMAP=ON
-DCURL_DISABLE_SMTP=ON
-DCURL_DISABLE_GOPHER=ON
-DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
${DEP_CMAKE_OPTS}
)
ExternalProject_Add(dep_wxwidgets
EXCLUDE_FROM_ALL 1
GIT_REPOSITORY "https://github.com/prusa3d/wxWidgets"
GIT_TAG v3.1.1-patched
# URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.1/wxWidgets-3.1.1.tar.bz2"
# URL_HASH SHA256=c925dfe17e8f8b09eb7ea9bfdcfcc13696a3e14e92750effd839f5e10726159e
# PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}\\wxwidgets-pngprefix.h" src\\png\\pngprefix.h
CMAKE_ARGS
-DBUILD_SHARED_LIBS=OFF
-DwxUSE_LIBPNG=builtin
-DwxUSE_ZLIB=builtin
-DwxUSE_OPENGL=ON
-DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
${DEP_CMAKE_OPTS}
)

View file

@ -1,6 +1,12 @@
# The unix common part expects DEP_CMAKE_OPTS to be set # The unix common part expects DEP_CMAKE_OPTS to be set
if (MINGW)
set(TBB_MINGW_WORKAROUND "-flifetime-dse=1")
else ()
set(TBB_MINGW_WORKAROUND "")
endif ()
ExternalProject_Add(dep_tbb ExternalProject_Add(dep_tbb
EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_ALL 1
URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz" URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz"
@ -8,6 +14,7 @@ ExternalProject_Add(dep_tbb
CMAKE_ARGS CMAKE_ARGS
-DTBB_BUILD_SHARED=OFF -DTBB_BUILD_SHARED=OFF
-DTBB_BUILD_TESTS=OFF -DTBB_BUILD_TESTS=OFF
-DCMAKE_CXX_FLAGS=${TBB_MINGW_WORKAROUND}
-DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
${DEP_CMAKE_OPTS} ${DEP_CMAKE_OPTS}
) )

View file

@ -19,6 +19,10 @@ else ()
message(FATAL_ERROR "Unsupported MSVC version") message(FATAL_ERROR "Unsupported MSVC version")
endif () endif ()
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(DEP_BOOST_TOOLSET "clang-win")
endif ()
if (${DEPS_BITS} EQUAL 32) if (${DEPS_BITS} EQUAL 32)
set(DEP_MSVC_GEN "Visual Studio ${DEP_VS_VER}") set(DEP_MSVC_GEN "Visual Studio ${DEP_VS_VER}")
set(DEP_PLATFORM "Win32") set(DEP_PLATFORM "Win32")

View file

@ -1 +1,2 @@
add_subdirectory(slabasebed) add_subdirectory(slabasebed)
add_subdirectory(slasupporttree)

View file

@ -5,6 +5,7 @@
#include <libslic3r/libslic3r.h> #include <libslic3r/libslic3r.h>
#include <libslic3r/TriangleMesh.hpp> #include <libslic3r/TriangleMesh.hpp>
#include <libslic3r/Tesselate.hpp> #include <libslic3r/Tesselate.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/SLA/SLABasePool.hpp> #include <libslic3r/SLA/SLABasePool.hpp>
#include <libslic3r/SLA/SLABoilerPlate.hpp> #include <libslic3r/SLA/SLABoilerPlate.hpp>
#include <libnest2d/tools/benchmark.h> #include <libnest2d/tools/benchmark.h>
@ -15,8 +16,8 @@ const std::string USAGE_STR = {
namespace Slic3r { namespace sla { namespace Slic3r { namespace sla {
Contour3D create_base_pool(const Polygons &ground_layer, Contour3D create_base_pool(const Polygons &ground_layer,
const Polygons &holes = {}, const ExPolygons &holes = {},
const PoolConfig& cfg = PoolConfig()); const PoolConfig& cfg = PoolConfig());
Contour3D walls(const Polygon& floor_plate, const Polygon& ceiling, Contour3D walls(const Polygon& floor_plate, const Polygon& ceiling,
@ -43,22 +44,22 @@ int main(const int argc, const char *argv[]) {
model.ReadSTLFile(argv[1]); model.ReadSTLFile(argv[1]);
model.align_to_origin(); model.align_to_origin();
Polygons ground_slice; ExPolygons ground_slice;
sla::base_plate(model, ground_slice, 0.1f); sla::base_plate(model, ground_slice, 0.1f);
if(ground_slice.empty()) return EXIT_FAILURE; if(ground_slice.empty()) return EXIT_FAILURE;
Polygon gndfirst; gndfirst = ground_slice.front(); ground_slice = offset_ex(ground_slice, 0.5);
sla::offset_with_breakstick_holes(gndfirst, 0.5, 10, 0.3); ExPolygon gndfirst; gndfirst = ground_slice.front();
sla::breakstick_holes(gndfirst, 0.5, 10, 0.3);
sla::Contour3D mesh; sla::Contour3D mesh;
bench.start(); bench.start();
sla::PoolConfig cfg; sla::PoolConfig cfg;
cfg.min_wall_height_mm = 0; cfg.min_wall_height_mm = 0;
cfg.edge_radius_mm = 0; cfg.edge_radius_mm = 0;
mesh = sla::create_base_pool(ground_slice, {}, cfg); mesh = sla::create_base_pool(to_polygons(ground_slice), {}, cfg);
bench.stop(); bench.stop();
@ -75,7 +76,7 @@ int main(const int argc, const char *argv[]) {
if(std::abs(a) < 1e-6) std::cout << "degenerate triangle" << std::endl; if(std::abs(a) < 1e-6) std::cout << "degenerate triangle" << std::endl;
} }
// basepool.write_ascii("out.stl"); // basepool.write_ascii("out.stl");
std::fstream outstream("out.obj", std::fstream::out); std::fstream outstream("out.obj", std::fstream::out);
mesh.to_obj(outstream); mesh.to_obj(outstream);

View file

@ -0,0 +1,2 @@
add_executable(slasupporttree slasupporttree.cpp)
target_link_libraries(slasupporttree libslic3r ${Boost_LIBRARIES} ${TBB_LIBRARIES} ${Boost_LIBRARIES} ${CMAKE_DL_LIBS})

View file

@ -0,0 +1,42 @@
#include <iostream>
#include <fstream>
#include <string>
#include <libslic3r/libslic3r.h>
#include <libslic3r/Model.hpp>
#include <libslic3r/Tesselate.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/SLA/SLAAutoSupports.hpp>
#include <libslic3r/SLA/SLASupportTree.hpp>
#include <libslic3r/SLAPrint.hpp>
#include <libslic3r/MTUtils.hpp>
#include <tbb/parallel_for.h>
#include <tbb/mutex.h>
#include <future>
const std::string USAGE_STR = {
"Usage: slasupporttree stlfilename.stl"
};
int main(const int argc, const char *argv[]) {
using namespace Slic3r;
using std::cout; using std::endl;
if(argc < 2) {
cout << USAGE_STR << endl;
return EXIT_SUCCESS;
}
DynamicPrintConfig config;
Model model = Model::read_from_file(argv[1], &config);
SLAPrint print;
print.apply(model, config);
print.process();
return EXIT_SUCCESS;
}

View file

@ -1,5 +1,6 @@
project(PrusaSlicer-native) project(PrusaSlicer-native)
add_subdirectory(build-utils)
add_subdirectory(admesh) add_subdirectory(admesh)
add_subdirectory(avrdude) add_subdirectory(avrdude)
# boost/nowide # boost/nowide
@ -47,7 +48,7 @@ if (SLIC3R_GUI)
endif () endif ()
endif () endif ()
else () else ()
find_package(wxWidgets 3.1 REQUIRED COMPONENTS base core adv html gl) find_package(wxWidgets 3.1 REQUIRED COMPONENTS html adv gl core base)
endif () endif ()
if(UNIX) if(UNIX)
@ -56,6 +57,9 @@ if (SLIC3R_GUI)
include(${wxWidgets_USE_FILE}) include(${wxWidgets_USE_FILE})
# list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc)
message(STATUS "wx libs: ${wxWidgets_LIBRARIES}")
add_subdirectory(slic3r) add_subdirectory(slic3r)
endif() endif()
@ -65,12 +69,18 @@ endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/PrusaSlicer.rc.in ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.rc @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/PrusaSlicer.rc.in ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.rc @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/PrusaSlicer.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.manifest @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/PrusaSlicer.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.manifest @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/osx/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/osx/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist @ONLY)
if (MSVC) if (WIN32)
add_library(PrusaSlicer SHARED PrusaSlicer.cpp PrusaSlicer.hpp) add_library(PrusaSlicer SHARED PrusaSlicer.cpp PrusaSlicer.hpp)
else () else ()
add_executable(PrusaSlicer PrusaSlicer.cpp PrusaSlicer.hpp) add_executable(PrusaSlicer PrusaSlicer.cpp PrusaSlicer.hpp)
endif () endif ()
if (NOT MSVC)
if (MINGW)
target_link_options(PrusaSlicer PUBLIC "-Wl,-allow-multiple-definition")
set_target_properties(PrusaSlicer PROPERTIES PREFIX "")
endif (MINGW)
if (NOT WIN32)
# Binary name on unix like systems (OSX, Linux) # Binary name on unix like systems (OSX, Linux)
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
endif () endif ()
@ -91,11 +101,12 @@ endif ()
# Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries. # Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries.
if (SLIC3R_GUI) if (SLIC3R_GUI)
target_link_libraries(PrusaSlicer libslic3r_gui ${wxWidgets_LIBRARIES}) # target_link_libraries(PrusaSlicer ws2_32 uxtheme setupapi libslic3r_gui ${wxWidgets_LIBRARIES})
target_link_libraries(PrusaSlicer libslic3r_gui ${wxWidgets_LIBRARIES})
# Configure libcurl and its dependencies OpenSSL & zlib # Configure libcurl and its dependencies OpenSSL & zlib
find_package(CURL REQUIRED) find_package(CURL REQUIRED)
if (NOT MSVC) if (NOT WIN32)
# Required by libcurl # Required by libcurl
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
endif() endif()
@ -123,7 +134,7 @@ if (SLIC3R_GUI)
target_link_options(PrusaSlicer PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>") target_link_options(PrusaSlicer PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
target_link_libraries(PrusaSlicer user32.lib Setupapi.lib OpenGL32.Lib GlU32.Lib) target_link_libraries(PrusaSlicer user32.lib Setupapi.lib OpenGL32.Lib GlU32.Lib)
elseif (MINGW) elseif (MINGW)
target_link_libraries(PrusaSlicer -lopengl32) target_link_libraries(PrusaSlicer opengl32 ws2_32 uxtheme setupapi)
elseif (APPLE) elseif (APPLE)
target_link_libraries(PrusaSlicer "-framework OpenGL") target_link_libraries(PrusaSlicer "-framework OpenGL")
else () else ()
@ -133,10 +144,16 @@ endif ()
# On Windows, a shim application is required to produce a console / non console version of the Slic3r application. # On Windows, a shim application is required to produce a console / non console version of the Slic3r application.
# Also the shim may load the Mesa software OpenGL renderer if the default renderer does not support OpenGL 2.0 and higher. # Also the shim may load the Mesa software OpenGL renderer if the default renderer does not support OpenGL 2.0 and higher.
if (MSVC) if (WIN32)
if (MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode")
endif()
add_executable(PrusaSlicer_app_gui WIN32 PrusaSlicer_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.rc) add_executable(PrusaSlicer_app_gui WIN32 PrusaSlicer_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.rc)
# Generate debug symbols even in release mode. # Generate debug symbols even in release mode.
target_link_options(PrusaSlicer_app_gui PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>") if(MSVC)
target_link_options(PrusaSlicer_app_gui PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
endif()
target_compile_definitions(PrusaSlicer_app_gui PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE) target_compile_definitions(PrusaSlicer_app_gui PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE)
add_dependencies(PrusaSlicer_app_gui PrusaSlicer) add_dependencies(PrusaSlicer_app_gui PrusaSlicer)
set_target_properties(PrusaSlicer_app_gui PROPERTIES OUTPUT_NAME "prusa-slicer") set_target_properties(PrusaSlicer_app_gui PROPERTIES OUTPUT_NAME "prusa-slicer")
@ -144,7 +161,9 @@ if (MSVC)
add_executable(PrusaSlicer_app_console PrusaSlicer_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.rc) add_executable(PrusaSlicer_app_console PrusaSlicer_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/PrusaSlicer.rc)
# Generate debug symbols even in release mode. # Generate debug symbols even in release mode.
if (MSVC)
target_link_options(PrusaSlicer_app_console PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>") target_link_options(PrusaSlicer_app_console PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
endif ()
target_compile_definitions(PrusaSlicer_app_console PRIVATE -DSLIC3R_WRAPPER_CONSOLE) target_compile_definitions(PrusaSlicer_app_console PRIVATE -DSLIC3R_WRAPPER_CONSOLE)
add_dependencies(PrusaSlicer_app_console PrusaSlicer) add_dependencies(PrusaSlicer_app_console PrusaSlicer)
set_target_properties(PrusaSlicer_app_console PROPERTIES OUTPUT_NAME "prusa-slicer-console") set_target_properties(PrusaSlicer_app_console PROPERTIES OUTPUT_NAME "prusa-slicer-console")
@ -152,7 +171,7 @@ if (MSVC)
endif () endif ()
# Link the resources dir to where Slic3r GUI expects it # Link the resources dir to where Slic3r GUI expects it
if (MSVC) if (WIN32)
if (CMAKE_CONFIGURATION_TYPES) if (CMAKE_CONFIGURATION_TYPES)
foreach (CONF ${CMAKE_CONFIGURATION_TYPES}) foreach (CONF ${CMAKE_CONFIGURATION_TYPES})
file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CONF}" WIN_CONF_OUTPUT_DIR) file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CONF}" WIN_CONF_OUTPUT_DIR)

View file

@ -7,8 +7,8 @@
#include <Windows.h> #include <Windows.h>
#include <wchar.h> #include <wchar.h>
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
extern "C" extern "C"
{ {
// Let the NVIDIA and AMD know we want to use their graphics card // Let the NVIDIA and AMD know we want to use their graphics card
// on a dual graphics card system. // on a dual graphics card system.
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
@ -54,14 +54,31 @@ using namespace Slic3r;
PrinterTechnology get_printer_technology(const DynamicConfig &config) PrinterTechnology get_printer_technology(const DynamicConfig &config)
{ {
const ConfigOptionEnum<PrinterTechnology> *opt = config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology"); const ConfigOptionEnum<PrinterTechnology> *opt = config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
return (opt == nullptr) ? ptUnknown : opt->value; return (opt == nullptr) ? ptUnknown : opt->value;
} }
int CLI::run(int argc, char **argv) int CLI::run(int argc, char **argv)
{ {
// Switch boost::filesystem to utf8. // Switch boost::filesystem to utf8.
boost::nowide::nowide_filesystem(); try {
boost::nowide::nowide_filesystem();
} catch (const std::runtime_error& ex) {
std::string caption = std::string(SLIC3R_APP_NAME) + " Error";
std::string text = std::string("An error occured while setting up locale.\n") + (
#if !defined(_WIN32) && !defined(__APPLE__)
// likely some linux system
"You may need to reconfigure the missing locales, likely by running the \"locale-gen\" and \"dpkg-reconfigure locales\" commands.\n"
#endif
SLIC3R_APP_NAME " will now terminate.\n\n") + ex.what();
#if defined(_WIN32) && defined(SLIC3R_GUI)
if (m_actions.empty())
// Empty actions means Slicer is executed in the GUI mode. Show a GUI message.
MessageBoxA(NULL, text.c_str(), caption.c_str(), MB_OK | MB_ICONERROR);
#endif
boost::nowide::cerr << text.c_str() << std::endl;
return 1;
}
if (! this->setup(argc, argv)) if (! this->setup(argc, argv))
return 1; return 1;
@ -71,14 +88,14 @@ int CLI::run(int argc, char **argv)
bool start_gui = m_actions.empty() && bool start_gui = m_actions.empty() &&
// cutting transformations are setting an "export" action. // cutting transformations are setting an "export" action.
std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() && std::find(m_transforms.begin(), m_transforms.end(), "cut") == m_transforms.end() &&
std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() && std::find(m_transforms.begin(), m_transforms.end(), "cut_x") == m_transforms.end() &&
std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end(); std::find(m_transforms.begin(), m_transforms.end(), "cut_y") == m_transforms.end();
PrinterTechnology printer_technology = get_printer_technology(m_extra_config); PrinterTechnology printer_technology = get_printer_technology(m_extra_config);
const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load", true)->values; const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load", true)->values;
// load config files supplied via --load // load config files supplied via --load
for (auto const &file : load_configs) { for (auto const &file : load_configs) {
if (! boost::filesystem::exists(file)) { if (! boost::filesystem::exists(file)) {
if (m_config.opt_bool("ignore_nonexistent_config")) { if (m_config.opt_bool("ignore_nonexistent_config")) {
continue; continue;
@ -104,7 +121,7 @@ int CLI::run(int argc, char **argv)
} }
m_print_config.apply(config); m_print_config.apply(config);
} }
// Read input file(s) if any. // Read input file(s) if any.
for (const std::string &file : m_input_files) { for (const std::string &file : m_input_files) {
if (! boost::filesystem::exists(file)) { if (! boost::filesystem::exists(file)) {
@ -140,21 +157,21 @@ int CLI::run(int argc, char **argv)
m_print_config.normalize(); m_print_config.normalize();
if (printer_technology == ptUnknown) if (printer_technology == ptUnknown)
printer_technology = std::find(m_actions.begin(), m_actions.end(), "export_sla") == m_actions.end() ? ptFFF : ptSLA; printer_technology = std::find(m_actions.begin(), m_actions.end(), "export_sla") == m_actions.end() ? ptFFF : ptSLA;
// Initialize full print configs for both the FFF and SLA technologies. // Initialize full print configs for both the FFF and SLA technologies.
FullPrintConfig fff_print_config; FullPrintConfig fff_print_config;
// SLAFullPrintConfig sla_print_config; // SLAFullPrintConfig sla_print_config;
fff_print_config.apply(m_print_config, true); fff_print_config.apply(m_print_config, true);
// sla_print_config.apply(m_print_config, true); // sla_print_config.apply(m_print_config, true);
// Loop through transform options. // Loop through transform options.
for (auto const &opt_key : m_transforms) { for (auto const &opt_key : m_transforms) {
if (opt_key == "merge") { if (opt_key == "merge") {
Model m; Model m;
for (auto &model : m_models) for (auto &model : m_models)
for (ModelObject *o : model.objects) for (ModelObject *o : model.objects)
m.add_object(*o); m.add_object(*o);
// Rearrange instances unless --dont-arrange is supplied // Rearrange instances unless --dont-arrange is supplied
if (! m_config.opt_bool("dont_arrange")) { if (! m_config.opt_bool("dont_arrange")) {
m.add_default_instances(); m.add_default_instances();
@ -166,8 +183,8 @@ int CLI::run(int argc, char **argv)
this->has_print_action() ? &bb : nullptr this->has_print_action() ? &bb : nullptr
); );
} }
m_models.clear(); m_models.clear();
m_models.emplace_back(std::move(m)); m_models.emplace_back(std::move(m));
} else if (opt_key == "duplicate") { } else if (opt_key == "duplicate") {
const BoundingBoxf &bb = fff_print_config.bed_shape.values; const BoundingBoxf &bb = fff_print_config.bed_shape.values;
for (auto &model : m_models) { for (auto &model : m_models) {
@ -196,11 +213,11 @@ int CLI::run(int argc, char **argv)
// this affects instances: // this affects instances:
model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value); model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value);
// this affects volumes: // this affects volumes:
//FIXME Vojtech: Who knows why the complete model should be aligned with Z as a single rigid body? //FIXME Vojtech: Who knows why the complete model should be aligned with Z as a single rigid body?
//model.align_to_ground(); //model.align_to_ground();
BoundingBoxf3 bbox; BoundingBoxf3 bbox;
for (ModelObject *model_object : model.objects) for (ModelObject *model_object : model.objects)
// We are interested into the Z span only, therefore it is sufficient to measure the bounding box of the 1st instance only. // We are interested into the Z span only, therefore it is sufficient to measure the bounding box of the 1st instance only.
bbox.merge(model_object->instance_bounding_box(0, false)); bbox.merge(model_object->instance_bounding_box(0, false));
for (ModelObject *model_object : model.objects) for (ModelObject *model_object : model.objects)
for (ModelInstance *model_instance : model_object->instances) for (ModelInstance *model_instance : model_object->instances)
@ -211,7 +228,7 @@ int CLI::run(int argc, char **argv)
for (auto &model : m_models) { for (auto &model : m_models) {
BoundingBoxf3 bb = model.bounding_box(); BoundingBoxf3 bb = model.bounding_box();
// this affects volumes: // this affects volumes:
model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z()); model.translate(-(bb.min.x() - p.x()), -(bb.min.y() - p.y()), -bb.min.z());
} }
} else if (opt_key == "dont_arrange") { } else if (opt_key == "dont_arrange") {
// do nothing - this option alters other transform options // do nothing - this option alters other transform options
@ -249,8 +266,8 @@ int CLI::run(int argc, char **argv)
std::vector<Model> new_models; std::vector<Model> new_models;
for (auto &model : m_models) { for (auto &model : m_models) {
model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0 model.translate(0, 0, -model.bounding_box().min.z()); // align to z = 0
size_t num_objects = model.objects.size(); size_t num_objects = model.objects.size();
for (size_t i = 0; i < num_objects; ++ i) { for (size_t i = 0; i < num_objects; ++ i) {
#if 0 #if 0
if (opt_key == "cut_x") { if (opt_key == "cut_x") {
@ -261,15 +278,15 @@ int CLI::run(int argc, char **argv)
o->cut(Z, m_config.opt_float("cut"), &out); o->cut(Z, m_config.opt_float("cut"), &out);
} }
#else #else
model.objects.front()->cut(0, m_config.opt_float("cut"), true, true, true); model.objects.front()->cut(0, m_config.opt_float("cut"), true, true, true);
#endif #endif
model.delete_object(size_t(0)); model.delete_object(size_t(0));
} }
} }
// TODO: copy less stuff around using pointers // TODO: copy less stuff around using pointers
m_models = new_models; m_models = new_models;
if (m_actions.empty()) if (m_actions.empty())
m_actions.push_back("export_stl"); m_actions.push_back("export_stl");
} }
@ -279,7 +296,7 @@ int CLI::run(int argc, char **argv)
for (auto &model : m_models) { for (auto &model : m_models) {
TriangleMesh mesh = model.mesh(); TriangleMesh mesh = model.mesh();
mesh.repair(); mesh.repair();
TriangleMeshPtrs meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value); TriangleMeshPtrs meshes = mesh.cut_by_grid(m_config.option<ConfigOptionPoint>("cut_grid")->value);
size_t i = 0; size_t i = 0;
for (TriangleMesh* m : meshes) { for (TriangleMesh* m : meshes) {
@ -290,10 +307,10 @@ int CLI::run(int argc, char **argv)
delete m; delete m;
} }
} }
// TODO: copy less stuff around using pointers // TODO: copy less stuff around using pointers
m_models = new_models; m_models = new_models;
if (m_actions.empty()) if (m_actions.empty())
m_actions.push_back("export_stl"); m_actions.push_back("export_stl");
} }
@ -307,7 +324,7 @@ int CLI::run(int argc, char **argv)
} }
} }
} else if (opt_key == "repair") { } else if (opt_key == "repair") {
// Models are repaired by default. // Models are repaired by default.
//for (auto &model : m_models) //for (auto &model : m_models)
// model.repair(); // model.repair();
} else { } else {
@ -315,7 +332,7 @@ int CLI::run(int argc, char **argv)
return 1; return 1;
} }
} }
// loop through action options // loop through action options
for (auto const &opt_key : m_actions) { for (auto const &opt_key : m_actions) {
if (opt_key == "help") { if (opt_key == "help") {
@ -358,14 +375,14 @@ int CLI::run(int argc, char **argv)
boost::nowide::cerr << "error: cannot export SLA slices for a SLA configuration" << std::endl; boost::nowide::cerr << "error: cannot export SLA slices for a SLA configuration" << std::endl;
return 1; return 1;
} }
// Make a copy of the model if the current action is not the last action, as the model may be // Make a copy of the model if the current action is not the last action, as the model may be
// modified by the centering and such. // modified by the centering and such.
Model model_copy; Model model_copy;
bool make_copy = &opt_key != &m_actions.back(); bool make_copy = &opt_key != &m_actions.back();
for (Model &model_in : m_models) { for (Model &model_in : m_models) {
if (make_copy) if (make_copy)
model_copy = model_in; model_copy = model_in;
Model &model = make_copy ? model_copy : model_in; Model &model = make_copy ? model_copy : model_in;
// If all objects have defined instances, their relative positions will be // If all objects have defined instances, their relative positions will be
// honored when printing (they will be only centered, unless --dont-arrange // honored when printing (they will be only centered, unless --dont-arrange
// is supplied); if any object has no instances, it will get a default one // is supplied); if any object has no instances, it will get a default one
@ -385,7 +402,7 @@ int CLI::run(int argc, char **argv)
if (! m_config.opt_bool("dont_arrange")) { if (! m_config.opt_bool("dont_arrange")) {
//FIXME make the min_object_distance configurable. //FIXME make the min_object_distance configurable.
model.arrange_objects(fff_print.config().min_object_distance()); model.arrange_objects(fff_print.config().min_object_distance());
model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value); model.center_instances_around_point(m_config.option<ConfigOptionPoint>("center")->value);
} }
if (printer_technology == ptFFF) { if (printer_technology == ptFFF) {
for (auto* mo : model.objects) for (auto* mo : model.objects)
@ -399,40 +416,40 @@ int CLI::run(int argc, char **argv)
} }
if (print->empty()) if (print->empty())
boost::nowide::cout << "Nothing to print for " << outfile << " . Either the print is empty or no object is fully inside the print volume." << std::endl; boost::nowide::cout << "Nothing to print for " << outfile << " . Either the print is empty or no object is fully inside the print volume." << std::endl;
else else
try { try {
std::string outfile_final; std::string outfile_final;
print->process(); print->process();
if (printer_technology == ptFFF) { if (printer_technology == ptFFF) {
// The outfile is processed by a PlaceholderParser. // The outfile is processed by a PlaceholderParser.
outfile = fff_print.export_gcode(outfile, nullptr); outfile = fff_print.export_gcode(outfile, nullptr);
outfile_final = fff_print.print_statistics().finalize_output_path(outfile); outfile_final = fff_print.print_statistics().finalize_output_path(outfile);
} else { } else {
outfile = sla_print.output_filepath(outfile); outfile = sla_print.output_filepath(outfile);
// We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata // We need to finalize the filename beforehand because the export function sets the filename inside the zip metadata
outfile_final = sla_print.print_statistics().finalize_output_path(outfile); outfile_final = sla_print.print_statistics().finalize_output_path(outfile);
sla_print.export_raster(outfile_final); sla_print.export_raster(outfile_final);
} }
if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final) != 0) { if (outfile != outfile_final && Slic3r::rename_file(outfile, outfile_final)) {
boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl; boost::nowide::cerr << "Renaming file " << outfile << " to " << outfile_final << " failed" << std::endl;
return 1; return 1;
} }
boost::nowide::cout << "Slicing result exported to " << outfile << std::endl; boost::nowide::cout << "Slicing result exported to " << outfile << std::endl;
} catch (const std::exception &ex) { } catch (const std::exception &ex) {
boost::nowide::cerr << ex.what() << std::endl; boost::nowide::cerr << ex.what() << std::endl;
return 1; return 1;
} }
/* /*
print.center = ! m_config.has("center") print.center = ! m_config.has("center")
&& ! m_config.has("align_xy") && ! m_config.has("align_xy")
&& ! m_config.opt_bool("dont_arrange"); && ! m_config.opt_bool("dont_arrange");
print.set_model(model); print.set_model(model);
// start chronometer // start chronometer
typedef std::chrono::high_resolution_clock clock_; typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_; typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> t0{ clock_::now() }; std::chrono::time_point<clock_> t0{ clock_::now() };
const std::string outfile = this->output_filepath(model, IO::Gcode); const std::string outfile = this->output_filepath(model, IO::Gcode);
try { try {
print.export_gcode(outfile); print.export_gcode(outfile);
@ -441,7 +458,7 @@ int CLI::run(int argc, char **argv)
return 1; return 1;
} }
boost::nowide::cout << "G-code exported to " << outfile << std::endl; boost::nowide::cout << "G-code exported to " << outfile << std::endl;
// output some statistics // output some statistics
double duration { std::chrono::duration_cast<second_>(clock_::now() - t0).count() }; double duration { std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
boost::nowide::cout << std::fixed << std::setprecision(0) boost::nowide::cout << std::fixed << std::setprecision(0)
@ -458,45 +475,45 @@ int CLI::run(int argc, char **argv)
return 1; return 1;
} }
} }
if (start_gui) { if (start_gui) {
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
// #ifdef USE_WX // #ifdef USE_WX
GUI::GUI_App *gui = new GUI::GUI_App(); GUI::GUI_App *gui = new GUI::GUI_App();
// gui->autosave = m_config.opt_string("autosave"); // gui->autosave = m_config.opt_string("autosave");
GUI::GUI_App::SetInstance(gui); GUI::GUI_App::SetInstance(gui);
gui->CallAfter([gui, this, &load_configs] { gui->CallAfter([gui, this, &load_configs] {
if (!gui->initialized()) { if (!gui->initialized()) {
return; return;
} }
#if 0 #if 0
// Load the cummulative config over the currently active profiles. // Load the cummulative config over the currently active profiles.
//FIXME if multiple configs are loaded, only the last one will have an effect. //FIXME if multiple configs are loaded, only the last one will have an effect.
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc). // We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
// As of now only the full configs are supported here. // As of now only the full configs are supported here.
if (!m_print_config.empty()) if (!m_print_config.empty())
gui->mainframe->load_config(m_print_config); gui->mainframe->load_config(m_print_config);
#endif #endif
if (! load_configs.empty()) if (! load_configs.empty())
// Load the last config to give it a name at the UI. The name of the preset may be later // Load the last config to give it a name at the UI. The name of the preset may be later
// changed by loading an AMF or 3MF. // changed by loading an AMF or 3MF.
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config. //FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
gui->mainframe->load_config_file(load_configs.back()); gui->mainframe->load_config_file(load_configs.back());
// If loading a 3MF file, the config is loaded from the last one. // If loading a 3MF file, the config is loaded from the last one.
if (! m_input_files.empty()) if (! m_input_files.empty())
gui->plater()->load_files(m_input_files, true, true); gui->plater()->load_files(m_input_files, true, true);
if (! m_extra_config.empty()) if (! m_extra_config.empty())
gui->mainframe->load_config(m_extra_config); gui->mainframe->load_config(m_extra_config);
}); });
return wxEntry(argc, argv); return wxEntry(argc, argv);
#else /* SLIC3R_GUI */ #else /* SLIC3R_GUI */
// No GUI support. Just print out a help. // No GUI support. Just print out a help.
this->print_help(false); this->print_help(false);
// If started without a parameter, consider it to be OK, otherwise report an error code (no action etc). // If started without a parameter, consider it to be OK, otherwise report an error code (no action etc).
return (argc == 0) ? 0 : 1; return (argc == 0) ? 0 : 1;
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
} }
return 0; return 0;
} }
@ -544,18 +561,18 @@ bool CLI::setup(int argc, char **argv)
// If any option is unsupported, print usage and abort immediately. // If any option is unsupported, print usage and abort immediately.
t_config_option_keys opt_order; t_config_option_keys opt_order;
if (! m_config.read_cli(argc, argv, &m_input_files, &opt_order)) { if (! m_config.read_cli(argc, argv, &m_input_files, &opt_order)) {
// Separate error message reported by the CLI parser from the help. // Separate error message reported by the CLI parser from the help.
boost::nowide::cerr << std::endl; boost::nowide::cerr << std::endl;
this->print_help(); this->print_help();
return false; return false;
}
// Parse actions and transform options.
for (auto const &opt_key : opt_order) {
if (cli_actions_config_def.has(opt_key))
m_actions.emplace_back(opt_key);
else if (cli_transform_config_def.has(opt_key))
m_transforms.emplace_back(opt_key);
} }
// Parse actions and transform options.
for (auto const &opt_key : opt_order) {
if (cli_actions_config_def.has(opt_key))
m_actions.emplace_back(opt_key);
else if (cli_transform_config_def.has(opt_key))
m_transforms.emplace_back(opt_key);
}
{ {
const ConfigOptionInt *opt_loglevel = m_config.opt<ConfigOptionInt>("loglevel"); const ConfigOptionInt *opt_loglevel = m_config.opt<ConfigOptionInt>("loglevel");
@ -568,15 +585,15 @@ bool CLI::setup(int argc, char **argv)
for (const std::pair<t_config_option_key, ConfigOptionDef> &optdef : *options) for (const std::pair<t_config_option_key, ConfigOptionDef> &optdef : *options)
m_config.optptr(optdef.first, true); m_config.optptr(optdef.first, true);
set_data_dir(m_config.opt_string("datadir")); set_data_dir(m_config.opt_string("datadir"));
return true; return true;
} }
void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const void CLI::print_help(bool include_print_options, PrinterTechnology printer_technology) const
{ {
boost::nowide::cout boost::nowide::cout
<< SLIC3R_BUILD_ID << " " << "based on Slic3r" << SLIC3R_BUILD_ID << " " << "based on Slic3r"
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
<< " (with GUI support)" << " (with GUI support)"
#else /* SLIC3R_GUI */ #else /* SLIC3R_GUI */
@ -588,20 +605,20 @@ void CLI::print_help(bool include_print_options, PrinterTechnology printer_techn
<< std::endl << std::endl
<< "Actions:" << std::endl; << "Actions:" << std::endl;
cli_actions_config_def.print_cli_help(boost::nowide::cout, false); cli_actions_config_def.print_cli_help(boost::nowide::cout, false);
boost::nowide::cout boost::nowide::cout
<< std::endl << std::endl
<< "Transform options:" << std::endl; << "Transform options:" << std::endl;
cli_transform_config_def.print_cli_help(boost::nowide::cout, false); cli_transform_config_def.print_cli_help(boost::nowide::cout, false);
boost::nowide::cout boost::nowide::cout
<< std::endl << std::endl
<< "Other options:" << std::endl; << "Other options:" << std::endl;
cli_misc_config_def.print_cli_help(boost::nowide::cout, false); cli_misc_config_def.print_cli_help(boost::nowide::cout, false);
if (include_print_options) { if (include_print_options) {
boost::nowide::cout << std::endl; boost::nowide::cout << std::endl;
print_config_def.print_cli_help(boost::nowide::cout, true, [printer_technology](const ConfigOptionDef &def) print_config_def.print_cli_help(boost::nowide::cout, true, [printer_technology](const ConfigOptionDef &def)
{ return printer_technology == ptAny || def.printer_technology == ptAny || printer_technology == def.printer_technology; }); { return printer_technology == ptAny || def.printer_technology == ptAny || printer_technology == def.printer_technology; });
} else { } else {
boost::nowide::cout boost::nowide::cout
@ -618,14 +635,14 @@ bool CLI::export_models(IO::ExportFormat format)
switch (format) { switch (format) {
case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr); break; case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr); break;
case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break; case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break;
case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break; case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break;
case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break; case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break;
default: assert(false); break; default: assert(false); break;
} }
if (success) if (success)
std::cout << "File exported to " << path << std::endl; std::cout << "File exported to " << path << std::endl;
else { else {
std::cerr << "File export to " << path << " failed" << std::endl; std::cerr << "File export to " << path << " failed" << std::endl;
return false; return false;
} }
} }
@ -639,12 +656,12 @@ std::string CLI::output_filepath(const Model &model, IO::ExportFormat format) co
case IO::AMF: ext = ".zip.amf"; break; case IO::AMF: ext = ".zip.amf"; break;
case IO::OBJ: ext = ".obj"; break; case IO::OBJ: ext = ".obj"; break;
case IO::STL: ext = ".stl"; break; case IO::STL: ext = ".stl"; break;
case IO::TMF: ext = ".3mf"; break; case IO::TMF: ext = ".3mf"; break;
default: assert(false); break; default: assert(false); break;
}; };
auto proposed_path = boost::filesystem::path(model.propose_export_file_name_and_path(ext)); auto proposed_path = boost::filesystem::path(model.propose_export_file_name_and_path(ext));
// use --output when available // use --output when available
std::string cmdline_param = m_config.opt_string("output"); std::string cmdline_param = m_config.opt_string("output");
if (! cmdline_param.empty()) { if (! cmdline_param.empty()) {
// if we were supplied a directory, use it and append our automatically generated filename // if we were supplied a directory, use it and append our automatically generated filename
boost::filesystem::path cmdline_path(cmdline_param); boost::filesystem::path cmdline_path(cmdline_param);
@ -656,20 +673,20 @@ std::string CLI::output_filepath(const Model &model, IO::ExportFormat format) co
return proposed_path.string(); return proposed_path.string();
} }
#ifdef _MSC_VER #if defined(_MSC_VER) || defined(__MINGW32__)
extern "C" { extern "C" {
__declspec(dllexport) int __stdcall slic3r_main(int argc, wchar_t **argv) __declspec(dllexport) int __stdcall slic3r_main(int argc, wchar_t **argv)
{ {
// Convert wchar_t arguments to UTF8. // Convert wchar_t arguments to UTF8.
std::vector<std::string> argv_narrow; std::vector<std::string> argv_narrow;
std::vector<char*> argv_ptrs(argc + 1, nullptr); std::vector<char*> argv_ptrs(argc + 1, nullptr);
for (size_t i = 0; i < argc; ++ i) for (size_t i = 0; i < argc; ++ i)
argv_narrow.emplace_back(boost::nowide::narrow(argv[i])); argv_narrow.emplace_back(boost::nowide::narrow(argv[i]));
for (size_t i = 0; i < argc; ++ i) for (size_t i = 0; i < argc; ++ i)
argv_ptrs[i] = const_cast<char*>(argv_narrow[i].data()); argv_ptrs[i] = const_cast<char*>(argv_narrow[i].data());
// Call the UTF8 main. // Call the UTF8 main.
return CLI().run(argc, argv_ptrs.data()); return CLI().run(argc, argv_ptrs.data());
} }
} }
#else /* _MSC_VER */ #else /* _MSC_VER */
int main(int argc, char **argv) int main(int argc, char **argv)

View file

@ -8,12 +8,12 @@
#include <wchar.h> #include <wchar.h>
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
extern "C" extern "C"
{ {
// Let the NVIDIA and AMD know we want to use their graphics card // Let the NVIDIA and AMD know we want to use their graphics card
// on a dual graphics card system. // on a dual graphics card system.
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
} }
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
@ -21,7 +21,7 @@ extern "C"
#include <stdio.h> #include <stdio.h>
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
#include <GL/GL.h> #include <GL/GL.h>
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
#include <string> #include <string>
@ -36,97 +36,97 @@ extern "C"
class OpenGLVersionCheck class OpenGLVersionCheck
{ {
public: public:
std::string version; std::string version;
std::string glsl_version; std::string glsl_version;
std::string vendor; std::string vendor;
std::string renderer; std::string renderer;
HINSTANCE hOpenGL = nullptr; HINSTANCE hOpenGL = nullptr;
bool success = false; bool success = false;
bool load_opengl_dll() bool load_opengl_dll()
{ {
MSG msg = {0}; MSG msg = {0};
WNDCLASS wc = {0}; WNDCLASS wc = {0};
wc.lpfnWndProc = OpenGLVersionCheck::supports_opengl2_wndproc; wc.lpfnWndProc = OpenGLVersionCheck::supports_opengl2_wndproc;
wc.hInstance = (HINSTANCE)GetModuleHandle(nullptr); wc.hInstance = (HINSTANCE)GetModuleHandle(nullptr);
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = L"PrusaSlicer_opengl_version_check"; wc.lpszClassName = L"PrusaSlicer_opengl_version_check";
wc.style = CS_OWNDC; wc.style = CS_OWNDC;
if (RegisterClass(&wc)) { if (RegisterClass(&wc)) {
HWND hwnd = CreateWindowW(wc.lpszClassName, L"PrusaSlicer_opengl_version_check", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, 0, 0, wc.hInstance, (LPVOID)this); HWND hwnd = CreateWindowW(wc.lpszClassName, L"PrusaSlicer_opengl_version_check", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, 0, 0, wc.hInstance, (LPVOID)this);
if (hwnd) { if (hwnd) {
message_pump_exit = false; message_pump_exit = false;
while (GetMessage(&msg, NULL, 0, 0 ) > 0 && ! message_pump_exit) while (GetMessage(&msg, NULL, 0, 0 ) > 0 && ! message_pump_exit)
DispatchMessage(&msg); DispatchMessage(&msg);
} }
} }
return this->success; return this->success;
} }
void unload_opengl_dll() void unload_opengl_dll()
{ {
if (this->hOpenGL) { if (this->hOpenGL) {
BOOL released = FreeLibrary(this->hOpenGL); BOOL released = FreeLibrary(this->hOpenGL);
if (released) if (released)
printf("System OpenGL library released\n"); printf("System OpenGL library released\n");
else else
printf("System OpenGL library NOT released\n"); printf("System OpenGL library NOT released\n");
this->hOpenGL = nullptr; this->hOpenGL = nullptr;
} }
} }
bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
{ {
// printf("is_version_greater_or_equal_to, version: %s\n", version.c_str()); // printf("is_version_greater_or_equal_to, version: %s\n", version.c_str());
std::vector<std::string> tokens; std::vector<std::string> tokens;
boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on); boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on);
if (tokens.empty()) if (tokens.empty())
return false; return false;
std::vector<std::string> numbers; std::vector<std::string> numbers;
boost::split(numbers, tokens[0], boost::is_any_of("."), boost::token_compress_on); boost::split(numbers, tokens[0], boost::is_any_of("."), boost::token_compress_on);
unsigned int gl_major = 0; unsigned int gl_major = 0;
unsigned int gl_minor = 0; unsigned int gl_minor = 0;
if (numbers.size() > 0) if (numbers.size() > 0)
gl_major = ::atoi(numbers[0].c_str()); gl_major = ::atoi(numbers[0].c_str());
if (numbers.size() > 1) if (numbers.size() > 1)
gl_minor = ::atoi(numbers[1].c_str()); gl_minor = ::atoi(numbers[1].c_str());
// printf("Major: %d, minor: %d\n", gl_major, gl_minor); // printf("Major: %d, minor: %d\n", gl_major, gl_minor);
if (gl_major < major) if (gl_major < major)
return false; return false;
else if (gl_major > major) else if (gl_major > major)
return true; return true;
else else
return gl_minor >= minor; return gl_minor >= minor;
} }
protected: protected:
static bool message_pump_exit; static bool message_pump_exit;
void check(HWND hWnd) void check(HWND hWnd)
{ {
hOpenGL = LoadLibraryExW(L"opengl32.dll", nullptr, 0); hOpenGL = LoadLibraryExW(L"opengl32.dll", nullptr, 0);
if (hOpenGL == nullptr) { if (hOpenGL == nullptr) {
printf("Failed loading the system opengl32.dll\n"); printf("Failed loading the system opengl32.dll\n");
return; return;
} }
typedef HGLRC (WINAPI *Func_wglCreateContext)(HDC); typedef HGLRC (WINAPI *Func_wglCreateContext)(HDC);
typedef BOOL (WINAPI *Func_wglMakeCurrent )(HDC, HGLRC); typedef BOOL (WINAPI *Func_wglMakeCurrent )(HDC, HGLRC);
typedef BOOL (WINAPI *Func_wglDeleteContext)(HGLRC); typedef BOOL (WINAPI *Func_wglDeleteContext)(HGLRC);
typedef GLubyte* (WINAPI *Func_glGetString )(GLenum); typedef GLubyte* (WINAPI *Func_glGetString )(GLenum);
Func_wglCreateContext wglCreateContext = (Func_wglCreateContext)GetProcAddress(hOpenGL, "wglCreateContext"); Func_wglCreateContext wglCreateContext = (Func_wglCreateContext)GetProcAddress(hOpenGL, "wglCreateContext");
Func_wglMakeCurrent wglMakeCurrent = (Func_wglMakeCurrent) GetProcAddress(hOpenGL, "wglMakeCurrent"); Func_wglMakeCurrent wglMakeCurrent = (Func_wglMakeCurrent) GetProcAddress(hOpenGL, "wglMakeCurrent");
Func_wglDeleteContext wglDeleteContext = (Func_wglDeleteContext)GetProcAddress(hOpenGL, "wglDeleteContext"); Func_wglDeleteContext wglDeleteContext = (Func_wglDeleteContext)GetProcAddress(hOpenGL, "wglDeleteContext");
Func_glGetString glGetString = (Func_glGetString) GetProcAddress(hOpenGL, "glGetString"); Func_glGetString glGetString = (Func_glGetString) GetProcAddress(hOpenGL, "glGetString");
if (wglCreateContext == nullptr || wglMakeCurrent == nullptr || wglDeleteContext == nullptr || glGetString == nullptr) { if (wglCreateContext == nullptr || wglMakeCurrent == nullptr || wglDeleteContext == nullptr || glGetString == nullptr) {
printf("Failed loading the system opengl32.dll: The library is invalid.\n"); printf("Failed loading the system opengl32.dll: The library is invalid.\n");
return; return;
} }
PIXELFORMATDESCRIPTOR pfd = PIXELFORMATDESCRIPTOR pfd =
{ {
@ -149,152 +149,155 @@ protected:
}; };
HDC ourWindowHandleToDeviceContext = ::GetDC(hWnd); HDC ourWindowHandleToDeviceContext = ::GetDC(hWnd);
// Gdi32.dll // Gdi32.dll
int letWindowsChooseThisPixelFormat = ::ChoosePixelFormat(ourWindowHandleToDeviceContext, &pfd); int letWindowsChooseThisPixelFormat = ::ChoosePixelFormat(ourWindowHandleToDeviceContext, &pfd);
// Gdi32.dll // Gdi32.dll
SetPixelFormat(ourWindowHandleToDeviceContext, letWindowsChooseThisPixelFormat, &pfd); SetPixelFormat(ourWindowHandleToDeviceContext, letWindowsChooseThisPixelFormat, &pfd);
// Opengl32.dll // Opengl32.dll
HGLRC glcontext = wglCreateContext(ourWindowHandleToDeviceContext); HGLRC glcontext = wglCreateContext(ourWindowHandleToDeviceContext);
wglMakeCurrent(ourWindowHandleToDeviceContext, glcontext); wglMakeCurrent(ourWindowHandleToDeviceContext, glcontext);
// Opengl32.dll // Opengl32.dll
const char *data = (const char*)glGetString(GL_VERSION); const char *data = (const char*)glGetString(GL_VERSION);
if (data != nullptr) if (data != nullptr)
this->version = data; this->version = data;
// printf("check -version: %s\n", version.c_str()); // printf("check -version: %s\n", version.c_str());
data = (const char*)glGetString(0x8B8C); // GL_SHADING_LANGUAGE_VERSION data = (const char*)glGetString(0x8B8C); // GL_SHADING_LANGUAGE_VERSION
if (data != nullptr) if (data != nullptr)
this->glsl_version = data; this->glsl_version = data;
data = (const char*)glGetString(GL_VENDOR); data = (const char*)glGetString(GL_VENDOR);
if (data != nullptr) if (data != nullptr)
this->vendor = data; this->vendor = data;
data = (const char*)glGetString(GL_RENDERER); data = (const char*)glGetString(GL_RENDERER);
if (data != nullptr) if (data != nullptr)
this->renderer = data; this->renderer = data;
// Opengl32.dll // Opengl32.dll
wglDeleteContext(glcontext); wglDeleteContext(glcontext);
::ReleaseDC(hWnd, ourWindowHandleToDeviceContext); ::ReleaseDC(hWnd, ourWindowHandleToDeviceContext);
this->success = true; this->success = true;
} }
static LRESULT CALLBACK supports_opengl2_wndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) static LRESULT CALLBACK supports_opengl2_wndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{ {
switch(message) switch(message)
{ {
case WM_CREATE: case WM_CREATE:
{ {
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam); CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
OpenGLVersionCheck *ogl_data = reinterpret_cast<OpenGLVersionCheck*>(pCreate->lpCreateParams); OpenGLVersionCheck *ogl_data = reinterpret_cast<OpenGLVersionCheck*>(pCreate->lpCreateParams);
ogl_data->check(hWnd); ogl_data->check(hWnd);
DestroyWindow(hWnd); DestroyWindow(hWnd);
return 0; return 0;
} }
case WM_NCDESTROY: case WM_NCDESTROY:
message_pump_exit = true; message_pump_exit = true;
return 0; return 0;
default: default:
return DefWindowProc(hWnd, message, wParam, lParam); return DefWindowProc(hWnd, message, wParam, lParam);
} }
} }
}; };
bool OpenGLVersionCheck::message_pump_exit = false; bool OpenGLVersionCheck::message_pump_exit = false;
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
extern "C" { extern "C" {
typedef int (__stdcall *Slic3rMainFunc)(int argc, wchar_t **argv); typedef int (__stdcall *Slic3rMainFunc)(int argc, wchar_t **argv);
Slic3rMainFunc slic3r_main = nullptr; Slic3rMainFunc slic3r_main = nullptr;
} }
extern "C" {
#ifdef SLIC3R_WRAPPER_NOCONSOLE #ifdef SLIC3R_WRAPPER_NOCONSOLE
int APIENTRY wWinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, PWSTR /* lpCmdLine */, int /* nCmdShow */) int APIENTRY wWinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, PWSTR /* lpCmdLine */, int /* nCmdShow */)
{ {
int argc; int argc;
wchar_t **argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); wchar_t **argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
#else #else
int wmain(int argc, wchar_t **argv) int wmain(int argc, wchar_t **argv)
{ {
#endif #endif
std::vector<wchar_t*> argv_extended; std::vector<wchar_t*> argv_extended;
argv_extended.emplace_back(argv[0]); argv_extended.emplace_back(argv[0]);
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
// Here one may push some additional parameters based on the wrapper type. // Here one may push some additional parameters based on the wrapper type.
bool force_mesa = false; bool force_mesa = false;
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
for (int i = 1; i < argc; ++ i) { for (int i = 1; i < argc; ++ i) {
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
if (wcscmp(argv[i], L"--sw-renderer") == 0) if (wcscmp(argv[i], L"--sw-renderer") == 0)
force_mesa = true; force_mesa = true;
else if (wcscmp(argv[i], L"--no-sw-renderer") == 0) else if (wcscmp(argv[i], L"--no-sw-renderer") == 0)
force_mesa = false; force_mesa = false;
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
argv_extended.emplace_back(argv[i]); argv_extended.emplace_back(argv[i]);
} }
argv_extended.emplace_back(nullptr); argv_extended.emplace_back(nullptr);
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
OpenGLVersionCheck opengl_version_check; OpenGLVersionCheck opengl_version_check;
bool load_mesa = bool load_mesa =
// Forced from the command line. // Forced from the command line.
force_mesa || force_mesa ||
// Running over a rempote desktop, and the RemoteFX is not enabled, therefore Windows will only provide SW OpenGL 1.1 context. // Running over a rempote desktop, and the RemoteFX is not enabled, therefore Windows will only provide SW OpenGL 1.1 context.
// In that case, use Mesa. // In that case, use Mesa.
::GetSystemMetrics(SM_REMOTESESSION) || ::GetSystemMetrics(SM_REMOTESESSION) ||
// Try to load the default OpenGL driver and test its context version. // Try to load the default OpenGL driver and test its context version.
! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0); ! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0);
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
wchar_t path_to_exe[MAX_PATH + 1] = { 0 }; wchar_t path_to_exe[MAX_PATH + 1] = { 0 };
::GetModuleFileNameW(nullptr, path_to_exe, MAX_PATH); ::GetModuleFileNameW(nullptr, path_to_exe, MAX_PATH);
wchar_t drive[_MAX_DRIVE]; wchar_t drive[_MAX_DRIVE];
wchar_t dir[_MAX_DIR]; wchar_t dir[_MAX_DIR];
wchar_t fname[_MAX_FNAME]; wchar_t fname[_MAX_FNAME];
wchar_t ext[_MAX_EXT]; wchar_t ext[_MAX_EXT];
_wsplitpath(path_to_exe, drive, dir, fname, ext); _wsplitpath(path_to_exe, drive, dir, fname, ext);
_wmakepath(path_to_exe, drive, dir, nullptr, nullptr); _wmakepath(path_to_exe, drive, dir, nullptr, nullptr);
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
// https://wiki.qt.io/Cross_compiling_Mesa_for_Windows // https://wiki.qt.io/Cross_compiling_Mesa_for_Windows
// http://download.qt.io/development_releases/prebuilt/llvmpipe/windows/ // http://download.qt.io/development_releases/prebuilt/llvmpipe/windows/
if (load_mesa) { if (load_mesa) {
opengl_version_check.unload_opengl_dll(); opengl_version_check.unload_opengl_dll();
wchar_t path_to_mesa[MAX_PATH + 1] = { 0 }; wchar_t path_to_mesa[MAX_PATH + 1] = { 0 };
wcscpy(path_to_mesa, path_to_exe); wcscpy(path_to_mesa, path_to_exe);
wcscat(path_to_mesa, L"mesa\\opengl32.dll"); wcscat(path_to_mesa, L"mesa\\opengl32.dll");
printf("Loading MESA OpenGL library: %S\n", path_to_mesa); printf("Loading MESA OpenGL library: %S\n", path_to_mesa);
HINSTANCE hInstance_OpenGL = LoadLibraryExW(path_to_mesa, nullptr, 0); HINSTANCE hInstance_OpenGL = LoadLibraryExW(path_to_mesa, nullptr, 0);
if (hInstance_OpenGL == nullptr) { if (hInstance_OpenGL == nullptr) {
printf("MESA OpenGL library was not loaded\n"); printf("MESA OpenGL library was not loaded\n");
} else } else
printf("MESA OpenGL library was loaded sucessfully\n"); printf("MESA OpenGL library was loaded sucessfully\n");
} }
#endif /* SLIC3R_GUI */ #endif /* SLIC3R_GUI */
wchar_t path_to_slic3r[MAX_PATH + 1] = { 0 }; wchar_t path_to_slic3r[MAX_PATH + 1] = { 0 };
wcscpy(path_to_slic3r, path_to_exe); wcscpy(path_to_slic3r, path_to_exe);
wcscat(path_to_slic3r, L"PrusaSlicer.dll"); wcscat(path_to_slic3r, L"PrusaSlicer.dll");
// printf("Loading Slic3r library: %S\n", path_to_slic3r); // printf("Loading Slic3r library: %S\n", path_to_slic3r);
HINSTANCE hInstance_Slic3r = LoadLibraryExW(path_to_slic3r, nullptr, 0); HINSTANCE hInstance_Slic3r = LoadLibraryExW(path_to_slic3r, nullptr, 0);
if (hInstance_Slic3r == nullptr) { if (hInstance_Slic3r == nullptr) {
printf("PrusaSlicer.dll was not loaded\n"); printf("PrusaSlicer.dll was not loaded\n");
return -1; return -1;
} }
// resolve function address here // resolve function address here
slic3r_main = (Slic3rMainFunc)GetProcAddress(hInstance_Slic3r, slic3r_main = (Slic3rMainFunc)GetProcAddress(hInstance_Slic3r,
#ifdef _WIN64 #ifdef _WIN64
// there is just a single calling conversion, therefore no mangling of the function name. // there is just a single calling conversion, therefore no mangling of the function name.
"slic3r_main" "slic3r_main"
#else // stdcall calling convention declaration #else // stdcall calling convention declaration
"_slic3r_main@8" "_slic3r_main@8"
#endif #endif
); );
if (slic3r_main == nullptr) { if (slic3r_main == nullptr) {
printf("could not locate the function slic3r_main in PrusaSlicer.dll\n"); printf("could not locate the function slic3r_main in PrusaSlicer.dll\n");
return -1; return -1;
} }
// argc minus the trailing nullptr of the argv // argc minus the trailing nullptr of the argv
return slic3r_main((int)argv_extended.size() - 1, argv_extended.data()); return slic3r_main((int)argv_extended.size() - 1, argv_extended.data());
}
} }

View file

@ -74,6 +74,10 @@ if (MSVC)
windows/unistd.cpp windows/unistd.cpp
windows/getopt.c windows/getopt.c
) )
elseif (MINGW)
set(AVRDUDE_SOURCES ${AVRDUDE_SOURCES}
windows/utf8.c
)
endif() endif()
add_executable(avrdude-conf-gen conf-generate.cpp) add_executable(avrdude-conf-gen conf-generate.cpp)
@ -81,13 +85,13 @@ add_executable(avrdude-conf-gen conf-generate.cpp)
# Config file embedding # Config file embedding
add_custom_command( add_custom_command(
DEPENDS avrdude-conf-gen ${CMAKE_CURRENT_SOURCE_DIR}/avrdude-slic3r.conf DEPENDS avrdude-conf-gen ${CMAKE_CURRENT_SOURCE_DIR}/avrdude-slic3r.conf
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/avrdude-slic3r.conf.h OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/avrdude-slic3r.conf.h
COMMAND $<TARGET_FILE:avrdude-conf-gen> avrdude-slic3r.conf avrdude_slic3r_conf > avrdude-slic3r.conf.h COMMAND $<TARGET_FILE:avrdude-conf-gen> avrdude-slic3r.conf avrdude_slic3r_conf ${CMAKE_CURRENT_BINARY_DIR}/avrdude-slic3r.conf.h
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
) )
add_custom_target(gen_conf_h add_custom_target(gen_conf_h
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/avrdude-slic3r.conf.h DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/avrdude-slic3r.conf.h
) )
add_library(avrdude STATIC ${AVRDUDE_SOURCES}) add_library(avrdude STATIC ${AVRDUDE_SOURCES})
@ -96,7 +100,15 @@ add_dependencies(avrdude gen_conf_h)
add_executable(avrdude-slic3r main-standalone.cpp) add_executable(avrdude-slic3r main-standalone.cpp)
target_link_libraries(avrdude-slic3r avrdude) target_link_libraries(avrdude-slic3r avrdude)
encoding_check(avrdude)
encoding_check(avrdude-slic3r)
# Make avrdude-slic3r.conf.h includable:
target_include_directories(avrdude SYSTEM PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
if (WIN32) if (WIN32)
target_compile_definitions(avrdude PRIVATE WIN32NATIVE=1) target_compile_definitions(avrdude PRIVATE WIN32NATIVE=1)
target_include_directories(avrdude SYSTEM PRIVATE windows) # So that sources find the getopt.h windows drop-in if(MSVC)
target_include_directories(avrdude SYSTEM PRIVATE windows) # So that sources find the getopt.h windows drop-in
endif(MSVC)
endif() endif()

File diff suppressed because it is too large Load diff

View file

@ -6,36 +6,42 @@
int main(int argc, char const *argv[]) int main(int argc, char const *argv[])
{ {
if (argc != 3) { if (argc != 4) {
std::cerr << "Usage: " << argv[0] << " <file> <symbol name>" << std::endl; std::cerr << "Usage: " << argv[0] << " <file> <symbol name> <output file>" << std::endl;
return -1; return -1;
} }
const char* filename = argv[1]; const char* filename_in = argv[1];
const char* symbol = argv[2]; const char* symbol = argv[2];
const char* filename_out = argv[3];
size_t size = 0; size_t size = 0;
std::fstream file(filename); std::fstream file(filename_in, std::ios::in | std::ios::binary);
if (!file.good()) { if (!file.good()) {
std::cerr << "Cannot read file: " << filename << std::endl; std::cerr << "Cannot read file: " << filename_in << std::endl;
} }
std::cout << "/* WARN: This file is auto-generated from `" << filename << "` */" << std::endl; std::fstream output(filename_out, std::ios::out | std::ios::trunc);
std::cout << "const unsigned char " << symbol << "[] = {"; if (!output.good()) {
std::cerr << "Cannot open output file: " << filename_out << std::endl;
}
output << "/* WARN: This file is auto-generated from `" << filename_in << "` */" << std::endl;
output << "const unsigned char " << symbol << "[] = {";
char c; char c;
std::cout << std::hex; output << std::hex;
std::cout.fill('0'); output.fill('0');
for (file.get(c); !file.eof(); size++, file.get(c)) { for (file.get(c); !file.eof(); size++, file.get(c)) {
if (size % 12 == 0) { std::cout << "\n "; } if (size % 12 == 0) { output << "\n "; }
std::cout << "0x" << std::setw(2) << (unsigned)c << ", "; output << "0x" << std::setw(2) << (unsigned)c << ", ";
} }
std::cout << "\n 0, 0\n};\n"; output << "\n 0, 0\n};\n";
std::cout << std::dec; output << std::dec;
std::cout << "const size_t " << symbol << "_size = " << size << ";" << std::endl; output << "const size_t " << symbol << "_size = " << size << ";" << std::endl;
std::cout << "const size_t " << symbol << "_size_yy = " << size + 2 << ";" << std::endl; output << "const size_t " << symbol << "_size_yy = " << size + 2 << ";" << std::endl;
return 0; return 0;
} }

View file

@ -30,7 +30,7 @@
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ /* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
* if you want the limit (max/min) macros for int types. * if you want the limit (max/min) macros for int types.

View file

@ -38,6 +38,10 @@ struct ArgvUtf8 : std::vector<char*>
} }
}; };
#endif
#ifdef _MSC_VER
int wmain(int argc_w, wchar_t *argv_w[]) int wmain(int argc_w, wchar_t *argv_w[])
{ {
ArgvUtf8 argv_utf8(argc_w, argv_w); ArgvUtf8 argv_utf8(argc_w, argv_w);

View file

@ -63,10 +63,15 @@ extern "C" {
#define STDOUT_FILENO 1 #define STDOUT_FILENO 1
#define STDERR_FILENO 2 #define STDERR_FILENO 2
#if defined(_MSC_VER) && defined(__clang__)
#include <stdint.h>
struct timezone;
struct timeval;
#else
#ifndef __cplusplus #ifndef __cplusplus
/* should be in some equivalent to <sys/types.h> */ /* should be in some equivalent to <sys/types.h> */
typedef __int8 int8_t; typedef __int8 int8_t;
typedef __int16 int16_t; typedef __int16 int16_t;
typedef __int32 int32_t; typedef __int32 int32_t;
typedef __int64 int64_t; typedef __int64 int64_t;
typedef unsigned __int8 uint8_t; typedef unsigned __int8 uint8_t;
@ -74,6 +79,7 @@ typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t; typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t; typedef unsigned __int64 uint64_t;
#endif #endif
#endif
int usleep(unsigned usec); int usleep(unsigned usec);

View file

@ -0,0 +1,39 @@
add_executable(encoding-check encoding-check.cpp)
# A global no-op target which depends on all encodings checks,
# and on which in turn all checked targets depend.
# This is done to make encoding checks the first thing to be
# performed before actually compiling any sources of the checked targets
# to make the check fail as early as possible.
add_custom_target(global-encoding-check
ALL
DEPENDS encoding-check
)
# Function that adds source file encoding check to a target
# using the above encoding-check binary
function(encoding_check TARGET)
# Obtain target source files
get_target_property(T_SOURCES ${TARGET} SOURCES)
# Define top-level encoding check target for this ${TARGET}
add_custom_target(encoding-check-${TARGET}
DEPENDS encoding-check ${T_SOURCES}
COMMENT "Checking source files encodings for target ${TARGET}"
)
# Add checking of each source file as a subcommand of encoding-check-${TARGET}
foreach(file ${T_SOURCES})
add_custom_command(TARGET encoding-check-${TARGET}
COMMAND $<TARGET_FILE:encoding-check> ${TARGET} ${file}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endforeach()
# This adds dependency on encoding-check-${TARGET} to ${TARET}
# via the global-encoding-check
add_dependencies(global-encoding-check encoding-check-${TARGET})
add_dependencies(${TARGET} global-encoding-check)
endfunction()

View file

@ -0,0 +1,119 @@
#include <vector>
#include <iostream>
#include <fstream>
#include <cstdlib>
/*
* The utf8_check() function scans the '\0'-terminated string starting
* at s. It returns a pointer to the first byte of the first malformed
* or overlong UTF-8 sequence found, or NULL if the string contains
* only correct UTF-8. It also spots UTF-8 sequences that could cause
* trouble if converted to UTF-16, namely surrogate characters
* (U+D800..U+DFFF) and non-Unicode positions (U+FFFE..U+FFFF). This
* routine is very likely to find a malformed sequence if the input
* uses any other encoding than UTF-8. It therefore can be used as a
* very effective heuristic for distinguishing between UTF-8 and other
* encodings.
*
* I wrote this code mainly as a specification of functionality; there
* are no doubt performance optimizations possible for certain CPUs.
*
* Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> -- 2005-03-30
* License: http://www.cl.cam.ac.uk/~mgk25/short-license.html
*/
unsigned char *utf8_check(unsigned char *s)
{
while (*s) {
if (*s < 0x80) {
// 0xxxxxxx
s++;
} else if ((s[0] & 0xe0) == 0xc0) {
// 110xxxxx 10xxxxxx
if ((s[1] & 0xc0) != 0x80 ||
(s[0] & 0xfe) == 0xc0) { // overlong?
return s;
} else {
s += 2;
}
} else if ((s[0] & 0xf0) == 0xe0) {
// 1110xxxx 10xxxxxx 10xxxxxx
if ((s[1] & 0xc0) != 0x80 ||
(s[2] & 0xc0) != 0x80 ||
(s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || // overlong?
(s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || // surrogate?
(s[0] == 0xef && s[1] == 0xbf &&
(s[2] & 0xfe) == 0xbe)) { // U+FFFE or U+FFFF?
return s;
} else {
s += 3;
}
} else if ((s[0] & 0xf8) == 0xf0) {
// 11110xxX 10xxxxxx 10xxxxxx 10xxxxxx
if ((s[1] & 0xc0) != 0x80 ||
(s[2] & 0xc0) != 0x80 ||
(s[3] & 0xc0) != 0x80 ||
(s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || // overlong?
(s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) { // > U+10FFFF?
return s;
} else {
s += 4;
}
} else {
return s;
}
}
return NULL;
}
int main(int argc, char const *argv[])
{
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " <program/library> <file>" << std::endl;
return -1;
}
const char* target = argv[1];
const char* filename = argv[2];
const auto error_exit = [=](const char* error) {
std::cerr << "\n\tError: " << error << ": " << filename << "\n"
<< "\tTarget: " << target << "\n"
<< std::endl;
std::exit(-2);
};
std::ifstream file(filename, std::ios::binary | std::ios::ate);
const auto size = file.tellg();
if (size == 0) {
return 0;
}
file.seekg(0, std::ios::beg);
std::vector<char> buffer(size);
if (file.read(buffer.data(), size)) {
buffer.push_back('\0');
// Check UTF-8 validity
if (utf8_check(reinterpret_cast<unsigned char*>(buffer.data())) != nullptr) {
error_exit("Source file does not contain (valid) UTF-8");
}
// Check against a BOM mark
if (buffer.size() >= 3
&& buffer[0] == '\xef'
&& buffer[1] == '\xbb'
&& buffer[2] == '\xbf') {
error_exit("Source file is valid UTF-8 but contains a BOM mark");
}
} else {
error_exit("Could not read source file");
}
return 0;
}

View file

@ -1,9 +1,9 @@
// This file is part of libigl, a simple c++ geometry processing library. // This file is part of libigl, a simple c++ geometry processing library.
// //
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com> // Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
// //
// This Source Code Form is subject to the terms of the Mozilla Public License // This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can // v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/. // obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_SORTABLE_ROW_H #ifndef IGL_SORTABLE_ROW_H
#define IGL_SORTABLE_ROW_H #define IGL_SORTABLE_ROW_H
@ -14,57 +14,53 @@
namespace igl namespace igl
{ {
// Templates: // Templates:
// T should be a matrix that implements .size(), and operator(int i) // T should be a matrix that implements .size(), and operator(int i)
template <typename T> template <typename T>
class SortableRow class SortableRow
{ {
public: public:
T data; T data;
public: public:
SortableRow():data(){}; SortableRow():data(){};
SortableRow(const T & data):data(data){}; SortableRow(const T & data):data(data){};
bool operator<(const SortableRow & that) const bool operator<(const SortableRow & that) const
{ {
// Get reference so that I can use parenthesis
const SortableRow<T> & THIS = *this;
// Lexicographical // Lexicographical
int minc = (THIS.data.size() < that.data.size()? int minc = (this->data.size() < that.data.size()?
THIS.data.size() : that.data.size()); this->data.size() : that.data.size());
// loop over columns // loop over columns
for(int i = 0;i<minc;i++) for(int i = 0;i<minc;i++)
{ {
if(THIS.data(i) == that.data(i)) if(this->data(i) == that.data(i))
{ {
continue; continue;
} }
return THIS.data(i) < that.data(i); return this->data(i) < that.data(i);
} }
// All characters the same, comes done to length // All characters the same, comes done to length
return THIS.data.size()<that.data.size(); return this->data.size()<that.data.size();
}; };
bool operator==(const SortableRow & that) const bool operator==(const SortableRow & that) const
{ {
// Get reference so that I can use parenthesis if(this->data.size() != that.data.size())
const SortableRow<T> & THIS = *this;
if(THIS.data.size() != that.data.size())
{ {
return false;
}
for(int i = 0;i<THIS.data.size();i++)
{
if(THIS.data(i) != that.data(i))
{
return false; return false;
} }
for(int i = 0;i<this->data.size();i++)
{
if(this->data(i) != that.data(i))
{
return false;
}
} }
return true; return true;
}; };
bool operator!=(const SortableRow & that) const bool operator!=(const SortableRow & that) const
{ {
return !(*this == that); return !(*this == that);
}; };
}; };
} }
#endif #endif

View file

@ -1,12 +1,12 @@
// This file is part of libigl, a simple c++ geometry processing library. // This file is part of libigl, a simple c++ geometry processing library.
// //
// Copyright (C) 2016 Alec Jacobson <alecjacobson@gmail.com> // Copyright (C) 2016 Alec Jacobson <alecjacobson@gmail.com>
// //
// This Source Code Form is subject to the terms of the Mozilla Public License // This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can // v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/. // obtain one at http://mozilla.org/MPL/2.0/.
#include "ray_box_intersect.h" #include "ray_box_intersect.h"
#include <vector> #include <array>
template < template <
typename Derivedsource, typename Derivedsource,
@ -27,7 +27,7 @@ IGL_INLINE bool igl::ray_box_intersect(
const Eigen::Vector3f& rayo, const Eigen::Vector3f& rayo,
const Eigen::Vector3f& rayd, const Eigen::Vector3f& rayd,
const Eigen::Vector3f& bmin, const Eigen::Vector3f& bmin,
const Eigen::Vector3f& bmax, const Eigen::Vector3f& bmax,
float & tnear, float & tnear,
float & tfar float & tfar
)->bool )->bool
@ -35,12 +35,12 @@ IGL_INLINE bool igl::ray_box_intersect(
Eigen::Vector3f bnear; Eigen::Vector3f bnear;
Eigen::Vector3f bfar; Eigen::Vector3f bfar;
// Checks for intersection testing on each direction coordinate // Checks for intersection testing on each direction coordinate
// Computes // Computes
float t1, t2; float t1, t2;
tnear = -1e+6f, tfar = 1e+6f; //, tCube; tnear = -1e+6f, tfar = 1e+6f; //, tCube;
bool intersectFlag = true; bool intersectFlag = true;
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
// std::cout << "coordinate " << i << ": bmin " << bmin(i) << ", bmax " << bmax(i) << std::endl; // std::cout << "coordinate " << i << ": bmin " << bmin(i) << ", bmax " << bmax(i) << std::endl;
assert(bmin(i) <= bmax(i)); assert(bmin(i) <= bmax(i));
if (::fabs(rayd(i)) < 1e-6) { // Ray parallel to axis i-th if (::fabs(rayd(i)) < 1e-6) { // Ray parallel to axis i-th
if (rayo(i) < bmin(i) || rayo(i) > bmax(i)) { if (rayo(i) < bmin(i) || rayo(i) > bmax(i)) {
@ -59,12 +59,12 @@ IGL_INLINE bool igl::ray_box_intersect(
} }
// std::cout << " bnear " << bnear(i) << ", bfar " << bfar(i) << std::endl; // std::cout << " bnear " << bnear(i) << ", bfar " << bfar(i) << std::endl;
// Finds the distance parameters t1 and t2 of the two ray-box intersections: // Finds the distance parameters t1 and t2 of the two ray-box intersections:
// t1 must be the closest to the ray origin rayo. // t1 must be the closest to the ray origin rayo.
t1 = (bnear(i) - rayo(i)) / rayd(i); t1 = (bnear(i) - rayo(i)) / rayd(i);
t2 = (bfar(i) - rayo(i)) / rayd(i); t2 = (bfar(i) - rayo(i)) / rayd(i);
if (t1 > t2) { if (t1 > t2) {
std::swap(t1,t2); std::swap(t1,t2);
} }
// The two intersection values are used to saturate tnear and tfar // The two intersection values are used to saturate tnear and tfar
if (t1 > tnear) { if (t1 > tnear) {
tnear = t1; tnear = t1;
@ -72,7 +72,7 @@ IGL_INLINE bool igl::ray_box_intersect(
if (t2 < tfar) { if (t2 < tfar) {
tfar = t2; tfar = t2;
} }
// std::cout << " t1 " << t1 << ", t2 " << t2 << ", tnear " << tnear << ", tfar " << tfar // std::cout << " t1 " << t1 << ", t2 " << t2 << ", tnear " << tnear << ", tfar " << tfar
// << " tnear > tfar? " << (tnear > tfar) << ", tfar < 0? " << (tfar < 0) << std::endl; // << " tnear > tfar? " << (tnear > tfar) << ", tfar < 0? " << (tfar < 0) << std::endl;
if(tnear > tfar) { if(tnear > tfar) {
intersectFlag = false; intersectFlag = false;
@ -101,11 +101,11 @@ IGL_INLINE bool igl::ray_box_intersect(
// This should be precomputed and provided as input // This should be precomputed and provided as input
typedef Matrix<Scalar,1,3> RowVector3S; typedef Matrix<Scalar,1,3> RowVector3S;
const RowVector3S inv_dir( 1./dir(0),1./dir(1),1./dir(2)); const RowVector3S inv_dir( 1./dir(0),1./dir(1),1./dir(2));
const std::vector<bool> sign = { inv_dir(0)<0, inv_dir(1)<0, inv_dir(2)<0}; const std::array<bool, 3> sign = { inv_dir(0)<0, inv_dir(1)<0, inv_dir(2)<0};
// http://people.csail.mit.edu/amy/papers/box-jgt.pdf // http://people.csail.mit.edu/amy/papers/box-jgt.pdf
// "An Efficient and Robust RayBox Intersection Algorithm" // "An Efficient and Robust RayBox Intersection Algorithm"
Scalar tymin, tymax, tzmin, tzmax; Scalar tymin, tymax, tzmin, tzmax;
std::vector<RowVector3S> bounds = {box.min(),box.max()}; std::array<RowVector3S, 2> bounds = {box.min(),box.max()};
tmin = ( bounds[sign[0]](0) - origin(0)) * inv_dir(0); tmin = ( bounds[sign[0]](0) - origin(0)) * inv_dir(0);
tmax = ( bounds[1-sign[0]](0) - origin(0)) * inv_dir(0); tmax = ( bounds[1-sign[0]](0) - origin(0)) * inv_dir(0);
tymin = (bounds[sign[1]](1) - origin(1)) * inv_dir(1); tymin = (bounds[sign[1]](1) - origin(1)) * inv_dir(1);
@ -146,4 +146,4 @@ IGL_INLINE bool igl::ray_box_intersect(
#ifdef IGL_STATIC_LIBRARY #ifdef IGL_STATIC_LIBRARY
template bool igl::ray_box_intersect<Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3>, double>(Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::AlignedBox<double, 3> const&, double const&, double const&, double&, double&); template bool igl::ray_box_intersect<Eigen::Matrix<double, 1, 3, 1, 1, 3>, Eigen::Matrix<double, 1, 3, 1, 1, 3>, double>(Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 1, 3, 1, 1, 3> > const&, Eigen::AlignedBox<double, 3> const&, double const&, double const&, double&, double&);
#endif #endif

View file

@ -29,7 +29,9 @@ IGL_INLINE bool igl::ray_mesh_intersect(
// Should be but can't be const // Should be but can't be const
Vector3d s_d = s.template cast<double>(); Vector3d s_d = s.template cast<double>();
Vector3d dir_d = dir.template cast<double>(); Vector3d dir_d = dir.template cast<double>();
hits.clear(); hits.clear();
hits.reserve(F.rows());
// loop over all triangles // loop over all triangles
for(int f = 0;f<F.rows();f++) for(int f = 0;f<F.rows();f++)
{ {

View file

@ -337,7 +337,15 @@ merge(const TMultiShape<PolygonImpl>& shapes)
//#define DISABLE_BOOST_SERIALIZE //#define DISABLE_BOOST_SERIALIZE
//#define DISABLE_BOOST_UNSERIALIZE //#define DISABLE_BOOST_UNSERIALIZE
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4244)
#pragma warning(disable: 4267)
#endif
// All other operators and algorithms are implemented with boost // All other operators and algorithms are implemented with boost
#include <libnest2d/utils/boost_alg.hpp> #include <libnest2d/utils/boost_alg.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // CLIPPER_BACKEND_HPP #endif // CLIPPER_BACKEND_HPP

View file

@ -7,6 +7,10 @@
#include "../tools/svgtools.hpp" #include "../tools/svgtools.hpp"
#include <libnest2d/utils/rotcalipers.hpp> #include <libnest2d/utils/rotcalipers.hpp>
#if defined(_MSC_VER) && defined(__clang__)
#define BOOST_NO_CXX17_HDR_STRING_VIEW
#endif
#include "boost/multiprecision/integer.hpp" #include "boost/multiprecision/integer.hpp"
#include "boost/rational.hpp" #include "boost/rational.hpp"

View file

@ -12,6 +12,11 @@
#include <ClipperUtils.hpp> #include <ClipperUtils.hpp>
#include <boost/geometry/index/rtree.hpp> #include <boost/geometry/index/rtree.hpp>
#if defined(_MSC_VER) && defined(__clang__)
#define BOOST_NO_CXX17_HDR_STRING_VIEW
#endif
#include <boost/multiprecision/integer.hpp> #include <boost/multiprecision/integer.hpp>
#include <boost/rational.hpp> #include <boost/rational.hpp>
@ -128,13 +133,24 @@ protected:
PConfig m_pconf; // Placement configuration PConfig m_pconf; // Placement configuration
TBin m_bin; TBin m_bin;
double m_bin_area; double m_bin_area;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4244)
#pragma warning(disable: 4267)
#endif
SpatIndex m_rtree; // spatial index for the normal (bigger) objects SpatIndex m_rtree; // spatial index for the normal (bigger) objects
SpatIndex m_smallsrtree; // spatial index for only the smaller items SpatIndex m_smallsrtree; // spatial index for only the smaller items
#ifdef _MSC_VER
#pragma warning(pop)
#endif
double m_norm; // A coefficient to scale distances double m_norm; // A coefficient to scale distances
MultiPolygon m_merged_pile; // The already merged pile (vector of items) MultiPolygon m_merged_pile; // The already merged pile (vector of items)
Box m_pilebb; // The bounding box of the merged pile. Box m_pilebb; // The bounding box of the merged pile.
ItemGroup m_remaining; // Remaining items (m_items at the beginning) ItemGroup m_remaining; // Remaining items
ItemGroup m_items; // The items to be packed ItemGroup m_items; // allready packed items
size_t m_item_count = 0; // Number of all items to be packed
template<class T> ArithmeticOnly<T, double> norm(T val) template<class T> ArithmeticOnly<T, double> norm(T val)
{ {
@ -152,7 +168,6 @@ protected:
const double bin_area = m_bin_area; const double bin_area = m_bin_area;
const SpatIndex& spatindex = m_rtree; const SpatIndex& spatindex = m_rtree;
const SpatIndex& smalls_spatindex = m_smallsrtree; const SpatIndex& smalls_spatindex = m_smallsrtree;
const ItemGroup& remaining = m_remaining;
// We will treat big items (compared to the print bed) differently // We will treat big items (compared to the print bed) differently
auto isBig = [bin_area](double a) { auto isBig = [bin_area](double a) {
@ -194,8 +209,8 @@ protected:
} compute_case; } compute_case;
bool bigitems = isBig(item.area()) || spatindex.empty(); bool bigitems = isBig(item.area()) || spatindex.empty();
if(bigitems && !remaining.empty()) compute_case = BIG_ITEM; if(bigitems && !m_remaining.empty()) compute_case = BIG_ITEM;
else if (bigitems && remaining.empty()) compute_case = LAST_BIG_ITEM; else if (bigitems && m_remaining.empty()) compute_case = LAST_BIG_ITEM;
else compute_case = SMALL_ITEM; else compute_case = SMALL_ITEM;
switch (compute_case) { switch (compute_case) {
@ -220,7 +235,7 @@ protected:
// The smalles distance from the arranged pile center: // The smalles distance from the arranged pile center:
double dist = norm(*(std::min_element(dists.begin(), dists.end()))); double dist = norm(*(std::min_element(dists.begin(), dists.end())));
double bindist = norm(pl::distance(ibb.center(), bincenter)); double bindist = norm(pl::distance(ibb.center(), bincenter));
dist = 0.8 * dist + 0.2*bindist; dist = 0.8 * dist + 0.2 * bindist;
// Prepare a variable for the alignment score. // Prepare a variable for the alignment score.
// This will indicate: how well is the candidate item // This will indicate: how well is the candidate item
@ -252,29 +267,24 @@ protected:
if(ascore < alignment_score) alignment_score = ascore; if(ascore < alignment_score) alignment_score = ascore;
} }
} }
density = std::sqrt(norm(fullbb.width()) * norm(fullbb.height())); density = std::sqrt(norm(fullbb.width()) * norm(fullbb.height()));
double R = double(m_remaining.size()) / m_item_count;
// The final mix of the score is the balance between the // The final mix of the score is the balance between the
// distance from the full pile center, the pack density and // distance from the full pile center, the pack density and
// the alignment with the neighbors // the alignment with the neighbors
if (result.empty()) if (result.empty())
score = 0.5 * dist + 0.5 * density; score = 0.50 * dist + 0.50 * density;
else else
score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score; score = R * 0.60 * dist +
(1.0 - R) * 0.20 * density +
0.20 * alignment_score;
break; break;
} }
case LAST_BIG_ITEM: { case LAST_BIG_ITEM: {
auto mp = m_merged_pile; score = norm(pl::distance(ibb.center(), m_pilebb.center()));
mp.emplace_back(item.transformedShape());
auto chull = sl::convexHull(mp);
placers::EdgeCache<clppr::Polygon> ec(chull);
double circ = norm(ec.circumference());
double bcirc = 2.0 * norm(fullbb.width() + fullbb.height());
score = 0.5 * circ + 0.5 * bcirc;
break; break;
} }
case SMALL_ITEM: { case SMALL_ITEM: {
@ -340,9 +350,11 @@ public:
m_pck.configure(m_pconf); m_pck.configure(m_pconf);
} }
template<class...Args> inline void operator()(Args&&...args) { template<class It> inline void operator()(It from, It to) {
m_rtree.clear(); /*m_preload_idx.clear();*/ m_rtree.clear();
m_pck.execute(std::forward<Args>(args)...); m_item_count += size_t(to - from);
m_pck.execute(from, to);
m_item_count = 0;
} }
inline void preload(std::vector<Item>& fixeditems) { inline void preload(std::vector<Item>& fixeditems) {
@ -361,6 +373,7 @@ public:
} }
m_pck.configure(m_pconf); m_pck.configure(m_pconf);
m_item_count += fixeditems.size();
} }
}; };
@ -424,6 +437,18 @@ inline Circle to_lnCircle(const CircleBed& circ) {
} }
// Get the type of bed geometry from a simple vector of points. // Get the type of bed geometry from a simple vector of points.
void BedShapeHint::reset(BedShapes type)
{
if (m_type != type) {
if (m_type == bsIrregular)
m_bed.polygon.Slic3r::Polyline::~Polyline();
else if (type == bsIrregular)
::new (&m_bed.polygon) Polyline();
}
m_type = type;
}
BedShapeHint::BedShapeHint(const Polyline &bed) { BedShapeHint::BedShapeHint(const Polyline &bed) {
auto x = [](const Point& p) { return p(X); }; auto x = [](const Point& p) { return p(X); };
auto y = [](const Point& p) { return p(Y); }; auto y = [](const Point& p) { return p(Y); };
@ -492,22 +517,50 @@ BedShapeHint::BedShapeHint(const Polyline &bed) {
m_type = BedShapes::bsCircle; m_type = BedShapes::bsCircle;
m_bed.circ = c; m_bed.circ = c;
} else { } else {
if (m_type == BedShapes::bsIrregular) assert(m_type != BedShapes::bsIrregular);
m_bed.polygon.Slic3r::Polyline::~Polyline();
m_type = BedShapes::bsIrregular; m_type = BedShapes::bsIrregular;
::new (&m_bed.polygon) Polyline(bed); ::new (&m_bed.polygon) Polyline(bed);
} }
} }
BedShapeHint &BedShapeHint::operator=(BedShapeHint &&cpy)
{
reset(cpy.m_type);
switch(m_type) {
case bsBox: m_bed.box = std::move(cpy.m_bed.box); break;
case bsCircle: m_bed.circ = std::move(cpy.m_bed.circ); break;
case bsIrregular: m_bed.polygon = std::move(cpy.m_bed.polygon); break;
case bsInfinite: m_bed.infbed = std::move(cpy.m_bed.infbed); break;
case bsUnknown: break;
}
return *this;
}
BedShapeHint &BedShapeHint::operator=(const BedShapeHint &cpy)
{
reset(cpy.m_type);
switch(m_type) {
case bsBox: m_bed.box = cpy.m_bed.box; break;
case bsCircle: m_bed.circ = cpy.m_bed.circ; break;
case bsIrregular: m_bed.polygon = cpy.m_bed.polygon; break;
case bsInfinite: m_bed.infbed = cpy.m_bed.infbed; break;
case bsUnknown: break;
}
return *this;
}
template<class BinT> // Arrange for arbitrary bin type template<class BinT> // Arrange for arbitrary bin type
void _arrange( void _arrange(
std::vector<Item> & shapes, std::vector<Item> & shapes,
std::vector<Item> & excludes, std::vector<Item> & excludes,
const BinT & bin, const BinT & bin,
coord_t minobjd, coord_t minobjd,
std::function<void(unsigned)> prind, std::function<void(unsigned)> prind,
std::function<bool()> stopfn) std::function<bool()> stopfn)
{ {
// Integer ceiling the min distance from the bed perimeters // Integer ceiling the min distance from the bed perimeters
coord_t md = minobjd - 2 * scaled(0.1 + EPSILON); coord_t md = minobjd - 2 * scaled(0.1 + EPSILON);

View file

@ -39,6 +39,9 @@ enum BedShapes {
class BedShapeHint { class BedShapeHint {
BedShapes m_type = BedShapes::bsInfinite; BedShapes m_type = BedShapes::bsInfinite;
// The union neither calls constructors nor destructors of its members.
// The only member with non-trivial constructor / destructor is the polygon,
// a placement new / delete needs to be called over it.
union BedShape_u { // TODO: use variant from cpp17? union BedShape_u { // TODO: use variant from cpp17?
CircleBed circ; CircleBed circ;
BoundingBox box; BoundingBox box;
@ -48,6 +51,9 @@ class BedShapeHint {
BedShape_u() {} BedShape_u() {}
} m_bed; } m_bed;
// Reset the type, allocate m_bed properly
void reset(BedShapes type);
public: public:
BedShapeHint(){} BedShapeHint(){}
@ -78,33 +84,8 @@ public:
BedShapeHint(const BedShapeHint &cpy) { *this = cpy; } BedShapeHint(const BedShapeHint &cpy) { *this = cpy; }
BedShapeHint(BedShapeHint &&cpy) { *this = std::move(cpy); } BedShapeHint(BedShapeHint &&cpy) { *this = std::move(cpy); }
BedShapeHint &operator=(const BedShapeHint &cpy) BedShapeHint &operator=(const BedShapeHint &cpy);
{ BedShapeHint& operator=(BedShapeHint &&cpy);
m_type = cpy.m_type;
switch(m_type) {
case bsBox: m_bed.box = cpy.m_bed.box; break;
case bsCircle: m_bed.circ = cpy.m_bed.circ; break;
case bsIrregular: m_bed.polygon = cpy.m_bed.polygon; break;
case bsInfinite: m_bed.infbed = cpy.m_bed.infbed; break;
case bsUnknown: break;
}
return *this;
}
BedShapeHint& operator=(BedShapeHint &&cpy)
{
m_type = cpy.m_type;
switch(m_type) {
case bsBox: m_bed.box = std::move(cpy.m_bed.box); break;
case bsCircle: m_bed.circ = std::move(cpy.m_bed.circ); break;
case bsIrregular: m_bed.polygon = std::move(cpy.m_bed.polygon); break;
case bsInfinite: m_bed.infbed = std::move(cpy.m_bed.infbed); break;
case bsUnknown: break;
}
return *this;
}
BedShapes get_type() const { return m_type; } BedShapes get_type() const { return m_type; }

View file

@ -5,6 +5,10 @@ include(PrecompiledHeader)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libslic3r_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h @ONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libslic3r_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h @ONLY)
if (MINGW)
add_compile_options(-Wa,-mbig-obj)
endif ()
add_library(libslic3r STATIC add_library(libslic3r STATIC
pchheader.cpp pchheader.cpp
pchheader.hpp pchheader.hpp
@ -70,7 +74,7 @@ add_library(libslic3r STATIC
GCode/CoolingBuffer.cpp GCode/CoolingBuffer.cpp
GCode/CoolingBuffer.hpp GCode/CoolingBuffer.hpp
GCode/PostProcessor.cpp GCode/PostProcessor.cpp
GCode/PostProcessor.hpp GCode/PostProcessor.hpp
# GCode/PressureEqualizer.cpp # GCode/PressureEqualizer.cpp
# GCode/PressureEqualizer.hpp # GCode/PressureEqualizer.hpp
GCode/PreviewData.cpp GCode/PreviewData.cpp
@ -123,6 +127,8 @@ add_library(libslic3r STATIC
Point.hpp Point.hpp
Polygon.cpp Polygon.cpp
Polygon.hpp Polygon.hpp
PolygonTrimmer.cpp
PolygonTrimmer.hpp
Polyline.cpp Polyline.cpp
Polyline.hpp Polyline.hpp
PolylineCollection.cpp PolylineCollection.cpp
@ -135,6 +141,7 @@ add_library(libslic3r STATIC
PrintConfig.hpp PrintConfig.hpp
PrintObject.cpp PrintObject.cpp
PrintRegion.cpp PrintRegion.cpp
Semver.cpp
SLAPrint.cpp SLAPrint.cpp
SLAPrint.hpp SLAPrint.hpp
SLA/SLAAutoSupports.hpp SLA/SLAAutoSupports.hpp
@ -182,6 +189,8 @@ add_library(libslic3r STATIC
SLA/SLARasterWriter.cpp SLA/SLARasterWriter.cpp
) )
encoding_check(libslic3r)
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE) add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
endif () endif ()

View file

@ -11,6 +11,7 @@
#include "libslic3r.h" #include "libslic3r.h"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "EdgeGrid.hpp" #include "EdgeGrid.hpp"
#include "Geometry.hpp"
#include "SVG.hpp" #include "SVG.hpp"
#if 0 #if 0
@ -275,134 +276,24 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
// 6) Finally fill in m_cell_data by rasterizing the lines once again. // 6) Finally fill in m_cell_data by rasterizing the lines once again.
for (size_t i = 0; i < m_cells.size(); ++i) for (size_t i = 0; i < m_cells.size(); ++i)
m_cells[i].end = m_cells[i].begin; m_cells[i].end = m_cells[i].begin;
for (size_t i = 0; i < m_contours.size(); ++i) {
const Slic3r::Points &pts = *m_contours[i]; struct Visitor {
for (size_t j = 0; j < pts.size(); ++j) { Visitor(std::vector<std::pair<size_t, size_t>> &cell_data, std::vector<Cell> &cells, size_t cols) :
// End points of the line segment. cell_data(cell_data), cells(cells), cols(cols), i(0), j(0) {}
Slic3r::Point p1(pts[j]);
Slic3r::Point p2 = pts[(j + 1 == pts.size()) ? 0 : j + 1]; void operator()(coord_t iy, coord_t ix) { cell_data[cells[iy*cols + ix].end++] = std::pair<size_t, size_t>(i, j); }
p1(0) -= m_bbox.min(0);
p1(1) -= m_bbox.min(1); std::vector<std::pair<size_t, size_t>> &cell_data;
p2(0) -= m_bbox.min(0); std::vector<Cell> &cells;
p2(1) -= m_bbox.min(1); size_t cols;
// Get the cells of the end points. size_t i;
coord_t ix = p1(0) / m_resolution; size_t j;
coord_t iy = p1(1) / m_resolution; } visitor(m_cell_data, m_cells, m_cols);
coord_t ixb = p2(0) / m_resolution;
coord_t iyb = p2(1) / m_resolution; for (; visitor.i < m_contours.size(); ++ visitor.i) {
assert(ix >= 0 && size_t(ix) < m_cols); const Slic3r::Points &pts = *m_contours[visitor.i];
assert(iy >= 0 && size_t(iy) < m_rows); for (; visitor.j < pts.size(); ++ visitor.j)
assert(ixb >= 0 && size_t(ixb) < m_cols); this->visit_cells_intersecting_line(pts[visitor.j], pts[(visitor.j + 1 == pts.size()) ? 0 : visitor.j + 1], visitor);
assert(iyb >= 0 && size_t(iyb) < m_rows);
// Account for the end points.
m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair<size_t, size_t>(i, j);
if (ix == ixb && iy == iyb)
// Both ends fall into the same cell.
continue;
// Raster the centeral part of the line.
coord_t dx = std::abs(p2(0) - p1(0));
coord_t dy = std::abs(p2(1) - p1(1));
if (p1(0) < p2(0)) {
int64_t ex = int64_t((ix + 1)*m_resolution - p1(0)) * int64_t(dy);
if (p1(1) < p2(1)) {
// x positive, y positive
int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx);
do {
assert(ix <= ixb && iy <= iyb);
if (ex < ey) {
ey -= ex;
ex = int64_t(dy) * m_resolution;
ix += 1;
}
else if (ex == ey) {
ex = int64_t(dy) * m_resolution;
ey = int64_t(dx) * m_resolution;
ix += 1;
iy += 1;
}
else {
assert(ex > ey);
ex -= ey;
ey = int64_t(dx) * m_resolution;
iy += 1;
}
m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair<size_t, size_t>(i, j);
} while (ix != ixb || iy != iyb);
}
else {
// x positive, y non positive
int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx);
do {
assert(ix <= ixb && iy >= iyb);
if (ex <= ey) {
ey -= ex;
ex = int64_t(dy) * m_resolution;
ix += 1;
}
else {
ex -= ey;
ey = int64_t(dx) * m_resolution;
iy -= 1;
}
m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair<size_t, size_t>(i, j);
} while (ix != ixb || iy != iyb);
}
}
else {
int64_t ex = int64_t(p1(0) - ix*m_resolution) * int64_t(dy);
if (p1(1) < p2(1)) {
// x non positive, y positive
int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx);
do {
assert(ix >= ixb && iy <= iyb);
if (ex < ey) {
ey -= ex;
ex = int64_t(dy) * m_resolution;
ix -= 1;
}
else {
assert(ex >= ey);
ex -= ey;
ey = int64_t(dx) * m_resolution;
iy += 1;
}
m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair<size_t, size_t>(i, j);
} while (ix != ixb || iy != iyb);
}
else {
// x non positive, y non positive
int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx);
do {
assert(ix >= ixb && iy >= iyb);
if (ex < ey) {
ey -= ex;
ex = int64_t(dy) * m_resolution;
ix -= 1;
}
else if (ex == ey) {
// The lower edge of a grid cell belongs to the cell.
// Handle the case where the ray may cross the lower left corner of a cell in a general case,
// or a left or lower edge in a degenerate case (horizontal or vertical line).
if (dx > 0) {
ex = int64_t(dy) * m_resolution;
ix -= 1;
}
if (dy > 0) {
ey = int64_t(dx) * m_resolution;
iy -= 1;
}
}
else {
assert(ex > ey);
ex -= ey;
ey = int64_t(dx) * m_resolution;
iy -= 1;
}
m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair<size_t, size_t>(i, j);
} while (ix != ixb || iy != iyb);
}
}
}
} }
} }
@ -1360,28 +1251,6 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) co
return out; return out;
} }
inline int segments_could_intersect(
const Slic3r::Point &ip1, const Slic3r::Point &ip2,
const Slic3r::Point &jp1, const Slic3r::Point &jp2)
{
Vec2i64 iv = (ip2 - ip1).cast<int64_t>();
Vec2i64 vij1 = (jp1 - ip1).cast<int64_t>();
Vec2i64 vij2 = (jp2 - ip1).cast<int64_t>();
int64_t tij1 = cross2(iv, vij1);
int64_t tij2 = cross2(iv, vij2);
int sij1 = (tij1 > 0) ? 1 : ((tij1 < 0) ? -1 : 0); // signum
int sij2 = (tij2 > 0) ? 1 : ((tij2 < 0) ? -1 : 0);
return sij1 * sij2;
}
inline bool segments_intersect(
const Slic3r::Point &ip1, const Slic3r::Point &ip2,
const Slic3r::Point &jp1, const Slic3r::Point &jp2)
{
return segments_could_intersect(ip1, ip2, jp1, jp2) <= 0 &&
segments_could_intersect(jp1, jp2, ip1, ip2) <= 0;
}
std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> EdgeGrid::Grid::intersecting_edges() const std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> EdgeGrid::Grid::intersecting_edges() const
{ {
std::vector<std::pair<ContourEdge, ContourEdge>> out; std::vector<std::pair<ContourEdge, ContourEdge>> out;
@ -1405,7 +1274,7 @@ std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>>
if (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) if (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2))
// Segments of the same contour share a common vertex. // Segments of the same contour share a common vertex.
continue; continue;
if (segments_intersect(ip1, ip2, jp1, jp2)) { if (Geometry::segments_intersect(ip1, ip2, jp1, jp2)) {
// The two segments intersect. Add them to the output. // The two segments intersect. Add them to the output.
int jfirst = (&jpts < &ipts) || (&jpts == &ipts && jpt < ipt); int jfirst = (&jpts < &ipts) || (&jpts == &ipts && jpt < ipt);
out.emplace_back(jfirst ? out.emplace_back(jfirst ?
@ -1440,7 +1309,7 @@ bool EdgeGrid::Grid::has_intersecting_edges() const
const Slic3r::Point &jp1 = jpts[jpt]; const Slic3r::Point &jp1 = jpts[jpt];
const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1]; const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1];
if (! (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) && if (! (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) &&
segments_intersect(ip1, ip2, jp1, jp2)) Geometry::segments_intersect(ip1, ip2, jp1, jp2))
return true; return true;
} }
} }

View file

@ -65,6 +65,145 @@ public:
std::vector<std::pair<ContourEdge, ContourEdge>> intersecting_edges() const; std::vector<std::pair<ContourEdge, ContourEdge>> intersecting_edges() const;
bool has_intersecting_edges() const; bool has_intersecting_edges() const;
template<typename FUNCTION> void visit_cells_intersecting_line(Slic3r::Point p1, Slic3r::Point p2, FUNCTION func) const
{
// End points of the line segment.
p1(0) -= m_bbox.min(0);
p1(1) -= m_bbox.min(1);
p2(0) -= m_bbox.min(0);
p2(1) -= m_bbox.min(1);
// Get the cells of the end points.
coord_t ix = p1(0) / m_resolution;
coord_t iy = p1(1) / m_resolution;
coord_t ixb = p2(0) / m_resolution;
coord_t iyb = p2(1) / m_resolution;
assert(ix >= 0 && size_t(ix) < m_cols);
assert(iy >= 0 && size_t(iy) < m_rows);
assert(ixb >= 0 && size_t(ixb) < m_cols);
assert(iyb >= 0 && size_t(iyb) < m_rows);
// Account for the end points.
func(iy, ix);
if (ix == ixb && iy == iyb)
// Both ends fall into the same cell.
return;
// Raster the centeral part of the line.
coord_t dx = std::abs(p2(0) - p1(0));
coord_t dy = std::abs(p2(1) - p1(1));
if (p1(0) < p2(0)) {
int64_t ex = int64_t((ix + 1)*m_resolution - p1(0)) * int64_t(dy);
if (p1(1) < p2(1)) {
// x positive, y positive
int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx);
do {
assert(ix <= ixb && iy <= iyb);
if (ex < ey) {
ey -= ex;
ex = int64_t(dy) * m_resolution;
ix += 1;
}
else if (ex == ey) {
ex = int64_t(dy) * m_resolution;
ey = int64_t(dx) * m_resolution;
ix += 1;
iy += 1;
}
else {
assert(ex > ey);
ex -= ey;
ey = int64_t(dx) * m_resolution;
iy += 1;
}
func(iy, ix);
} while (ix != ixb || iy != iyb);
}
else {
// x positive, y non positive
int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx);
do {
assert(ix <= ixb && iy >= iyb);
if (ex <= ey) {
ey -= ex;
ex = int64_t(dy) * m_resolution;
ix += 1;
}
else {
ex -= ey;
ey = int64_t(dx) * m_resolution;
iy -= 1;
}
func(iy, ix);
} while (ix != ixb || iy != iyb);
}
}
else {
int64_t ex = int64_t(p1(0) - ix*m_resolution) * int64_t(dy);
if (p1(1) < p2(1)) {
// x non positive, y positive
int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx);
do {
assert(ix >= ixb && iy <= iyb);
if (ex < ey) {
ey -= ex;
ex = int64_t(dy) * m_resolution;
ix -= 1;
}
else {
assert(ex >= ey);
ex -= ey;
ey = int64_t(dx) * m_resolution;
iy += 1;
}
func(iy, ix);
} while (ix != ixb || iy != iyb);
}
else {
// x non positive, y non positive
int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx);
do {
assert(ix >= ixb && iy >= iyb);
if (ex < ey) {
ey -= ex;
ex = int64_t(dy) * m_resolution;
ix -= 1;
}
else if (ex == ey) {
// The lower edge of a grid cell belongs to the cell.
// Handle the case where the ray may cross the lower left corner of a cell in a general case,
// or a left or lower edge in a degenerate case (horizontal or vertical line).
if (dx > 0) {
ex = int64_t(dy) * m_resolution;
ix -= 1;
}
if (dy > 0) {
ey = int64_t(dx) * m_resolution;
iy -= 1;
}
}
else {
assert(ex > ey);
ex -= ey;
ey = int64_t(dx) * m_resolution;
iy -= 1;
}
func(iy, ix);
} while (ix != ixb || iy != iyb);
}
}
}
std::pair<std::vector<std::pair<size_t, size_t>>::const_iterator, std::vector<std::pair<size_t, size_t>>::const_iterator> cell_data_range(coord_t row, coord_t col) const
{
const EdgeGrid::Grid::Cell &cell = m_cells[row * m_cols + col];
return std::make_pair(m_cell_data.begin() + cell.begin, m_cell_data.begin() + cell.end);
}
std::pair<const Slic3r::Point&, const Slic3r::Point&> segment(const std::pair<size_t, size_t> &contour_and_segment_idx) const
{
const Slic3r::Points &ipts = *m_contours[contour_and_segment_idx.first];
size_t ipt = contour_and_segment_idx.second;
return std::pair<const Slic3r::Point&, const Slic3r::Point&>(ipts[ipt], ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1]);
}
protected: protected:
struct Cell { struct Cell {
Cell() : begin(0), end(0) {} Cell() : begin(0), end(0) {}

View file

@ -197,9 +197,6 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation);
// Disable linear advance for the wipe tower operations.
gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
if (!tcr.priming) { if (!tcr.priming) {
// Move over the wipe tower. // Move over the wipe tower.
// Retract for a tool change, using the toolchange retract value and setting the priming extra length. // Retract for a tool change, using the toolchange retract value and setting the priming extra length.
@ -474,8 +471,10 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
if (layer_to_print.print_z() > maximal_print_z + EPSILON) if (layer_to_print.print_z() > maximal_print_z + EPSILON)
throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" + throw std::runtime_error(_(L("Empty layers detected, the output would not be printable.")) + "\n\n" +
_(L("Object name: ")) + object.model_object()->name + "\n" + _(L("Print z: ")) + _(L("Object name: ")) + object.model_object()->name + "\n" + _(L("Print z: ")) +
std::to_string(layers_to_print.back().print_z())); std::to_string(layers_to_print.back().print_z()) + "\n\n" + _(L("This is "
"usually caused by negligibly small extrusions or by a faulty model. Try to repair "
" the model or change its orientation on the bed.")));
// Remember last layer with extrusions. // Remember last layer with extrusions.
last_extrusion_layer = &layers_to_print.back(); last_extrusion_layer = &layers_to_print.back();
} }
@ -608,7 +607,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
m_analyzer.reset(); m_analyzer.reset();
} }
if (rename_file(path_tmp, path) != 0) if (rename_file(path_tmp, path))
throw std::runtime_error( throw std::runtime_error(
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' +
"Is " + path_tmp + " locked?" + '\n'); "Is " + path_tmp + " locked?" + '\n');
@ -2888,7 +2887,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
// Set the temperature if the wipe tower didn't (not needed for non-single extruder MM) // Set the temperature if the wipe tower didn't (not needed for non-single extruder MM)
if (m_config.single_extruder_multi_material && !m_config.wipe_tower) { if (m_config.single_extruder_multi_material && !m_config.wipe_tower) {
int temp = (m_layer_index == 0 ? m_config.first_layer_temperature.get_at(extruder_id) : int temp = (m_layer_index <= 0 ? m_config.first_layer_temperature.get_at(extruder_id) :
m_config.temperature.get_at(extruder_id)); m_config.temperature.get_at(extruder_id));
gcode += m_writer.set_temperature(temp, false); gcode += m_writer.set_temperature(temp, false);

View file

@ -101,23 +101,6 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
this->collect_extruder_statistics(prime_multi_material); this->collect_extruder_statistics(prime_multi_material);
} }
LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z)
{
auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), LayerTools(print_z - EPSILON));
assert(it_layer_tools != m_layer_tools.end());
coordf_t dist_min = std::abs(it_layer_tools->print_z - print_z);
for (++ it_layer_tools; it_layer_tools != m_layer_tools.end(); ++it_layer_tools) {
coordf_t d = std::abs(it_layer_tools->print_z - print_z);
if (d >= dist_min)
break;
dist_min = d;
}
-- it_layer_tools;
assert(dist_min < EPSILON);
return *it_layer_tools;
}
void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs) void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
{ {
sort_remove_duplicates(zs); sort_remove_duplicates(zs);
@ -156,7 +139,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
LayerTools &layer_tools = this->tools_for_layer(layer->print_z); LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
// What extruders are required to print this object layer? // What extruders are required to print this object layer?
for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) {
const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr;
if (layerm == nullptr) if (layerm == nullptr)
continue; continue;
const PrintRegion &region = *object.print()->regions()[region_id]; const PrintRegion &region = *object.print()->regions()[region_id];
@ -336,10 +319,10 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
// If it is a bug, it's likely not critical, because this code is unchanged for a long time. It might // If it is a bug, it's likely not critical, because this code is unchanged for a long time. It might
// still be worth looking into it more and decide if it is a bug or an obsolete assert. // still be worth looking into it more and decide if it is a bug or an obsolete assert.
//assert(lt_prev.extruders.back() == lt_next.extruders.front()); //assert(lt_prev.extruders.back() == lt_next.extruders.front());
lt_extra.has_wipe_tower = true; lt_extra.has_wipe_tower = true;
lt_extra.extruders.push_back(lt_next.extruders.front()); lt_extra.extruders.push_back(lt_next.extruders.front());
lt_extra.wipe_tower_partitions = lt_next.wipe_tower_partitions; lt_extra.wipe_tower_partitions = lt_next.wipe_tower_partitions;
} }
} }
} }
break; break;
@ -381,7 +364,7 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
// Reorder m_all_printing_extruders in the sequence they will be primed, the last one will be m_first_printing_extruder. // Reorder m_all_printing_extruders in the sequence they will be primed, the last one will be m_first_printing_extruder.
// Then set m_first_printing_extruder to the 1st extruder primed. // Then set m_first_printing_extruder to the 1st extruder primed.
m_all_printing_extruders.erase( m_all_printing_extruders.erase(
std::remove_if(m_all_printing_extruders.begin(), m_all_printing_extruders.end(), std::remove_if(m_all_printing_extruders.begin(), m_all_printing_extruders.end(),
[ this ](const unsigned int eid) { return eid == m_first_printing_extruder; }), [ this ](const unsigned int eid) { return eid == m_first_printing_extruder; }),
m_all_printing_extruders.end()); m_all_printing_extruders.end());
m_all_printing_extruders.emplace_back(m_first_printing_extruder); m_all_printing_extruders.emplace_back(m_first_printing_extruder);
@ -629,6 +612,6 @@ const std::vector<int>* WipingExtrusions::get_extruder_overrides(const Extrusion
return &(entity_map_it->second); return &(entity_map_it->second);
} }
} // namespace Slic3r } // namespace Slic3r

View file

@ -102,45 +102,60 @@ private:
class ToolOrdering class ToolOrdering
{ {
public: public:
ToolOrdering() {} ToolOrdering() {}
// For the use case when each object is printed separately // For the use case when each object is printed separately
// (print.config.complete_objects is true). // (print.config.complete_objects is true).
ToolOrdering(const PrintObject &object, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false); ToolOrdering(const PrintObject &object, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false);
// For the use case when all objects are printed at once. // For the use case when all objects are printed at once.
// (print.config.complete_objects is false). // (print.config.complete_objects is false).
ToolOrdering(const Print &print, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false); ToolOrdering(const Print &print, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false);
void clear() { m_layer_tools.clear(); } void clear() { m_layer_tools.clear(); }
// Get the first extruder printing, including the extruder priming areas, returns -1 if there is no layer printed. // Get the first extruder printing, including the extruder priming areas, returns -1 if there is no layer printed.
unsigned int first_extruder() const { return m_first_printing_extruder; } unsigned int first_extruder() const { return m_first_printing_extruder; }
// Get the first extruder printing the layer_tools, returns -1 if there is no layer printed. // Get the first extruder printing the layer_tools, returns -1 if there is no layer printed.
unsigned int last_extruder() const { return m_last_printing_extruder; } unsigned int last_extruder() const { return m_last_printing_extruder; }
// For a multi-material print, the printing extruders are ordered in the order they shall be primed. // For a multi-material print, the printing extruders are ordered in the order they shall be primed.
const std::vector<unsigned int>& all_extruders() const { return m_all_printing_extruders; } const std::vector<unsigned int>& all_extruders() const { return m_all_printing_extruders; }
// Find LayerTools with the closest print_z. template<class Self> static auto tools_for_layer(Self& self, coordf_t print_z) -> decltype (*self.m_layer_tools.begin())
LayerTools& tools_for_layer(coordf_t print_z); {
const LayerTools& tools_for_layer(coordf_t print_z) const auto it_layer_tools = std::lower_bound(self.m_layer_tools.begin(), self.m_layer_tools.end(), LayerTools(print_z - EPSILON));
{ return *const_cast<const LayerTools*>(&const_cast<const ToolOrdering*>(this)->tools_for_layer(print_z)); } assert(it_layer_tools != self.m_layer_tools.end());
coordf_t dist_min = std::abs(it_layer_tools->print_z - print_z);
for (++ it_layer_tools; it_layer_tools != self.m_layer_tools.end(); ++it_layer_tools) {
coordf_t d = std::abs(it_layer_tools->print_z - print_z);
if (d >= dist_min)
break;
dist_min = d;
}
-- it_layer_tools;
assert(dist_min < EPSILON);
return *it_layer_tools;
}
const LayerTools& front() const { return m_layer_tools.front(); } // Find LayerTools with the closest print_z.
const LayerTools& back() const { return m_layer_tools.back(); } LayerTools& tools_for_layer(coordf_t print_z) { return tools_for_layer(*this, print_z); }
std::vector<LayerTools>::const_iterator begin() const { return m_layer_tools.begin(); } const LayerTools& tools_for_layer(coordf_t print_z) const { return tools_for_layer(*this, print_z); }
std::vector<LayerTools>::const_iterator end() const { return m_layer_tools.end(); }
bool empty() const { return m_layer_tools.empty(); } const LayerTools& front() const { return m_layer_tools.front(); }
std::vector<LayerTools>& layer_tools() { return m_layer_tools; } const LayerTools& back() const { return m_layer_tools.back(); }
bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; } std::vector<LayerTools>::const_iterator begin() const { return m_layer_tools.begin(); }
std::vector<LayerTools>::const_iterator end() const { return m_layer_tools.end(); }
bool empty() const { return m_layer_tools.empty(); }
std::vector<LayerTools>& layer_tools() { return m_layer_tools; }
bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; }
private: private:
void initialize_layers(std::vector<coordf_t> &zs); void initialize_layers(std::vector<coordf_t> &zs);
void collect_extruders(const PrintObject &object); void collect_extruders(const PrintObject &object);
void reorder_extruders(unsigned int last_extruder_id); void reorder_extruders(unsigned int last_extruder_id);
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z); void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
void collect_extruder_statistics(bool prime_multi_material); void collect_extruder_statistics(bool prime_multi_material);
std::vector<LayerTools> m_layer_tools; std::vector<LayerTools> m_layer_tools;

View file

@ -22,6 +22,7 @@ TODO LIST
#include <numeric> #include <numeric>
#include "Analyzer.hpp" #include "Analyzer.hpp"
#include "BoundingBox.hpp"
#if defined(__linux) || defined(__GNUC__ ) #if defined(__linux) || defined(__GNUC__ )
#include <strings.h> #include <strings.h>
@ -113,6 +114,11 @@ public:
return (*this); return (*this);
} }
WipeTowerWriter& disable_linear_advance() {
m_gcode += (m_gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
return *this;
}
// Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various // Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various
// filament loading and cooling moves from normal extrusion moves. Therefore the writer // filament loading and cooling moves from normal extrusion moves. Therefore the writer
// is asked to suppres output of some lines, which look like extrusions. // is asked to suppres output of some lines, which look like extrusions.
@ -470,6 +476,83 @@ private:
WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool) :
m_semm(config.single_extruder_multi_material.value),
m_wipe_tower_pos(config.wipe_tower_x, config.wipe_tower_y),
m_wipe_tower_width(config.wipe_tower_width),
m_wipe_tower_rotation_angle(config.wipe_tower_rotation_angle),
m_y_shift(0.f),
m_z_pos(0.f),
m_is_first_layer(false),
m_bridging(config.wipe_tower_bridging),
m_gcode_flavor(config.gcode_flavor),
m_current_tool(initial_tool),
wipe_volumes(wiping_matrix)
{
// If this is a single extruder MM printer, we will use all the SE-specific config values.
// Otherwise, the defaults will be used to turn off the SE stuff.
if (m_semm) {
m_cooling_tube_retraction = config.cooling_tube_retraction;
m_cooling_tube_length = config.cooling_tube_length;
m_parking_pos_retraction = config.parking_pos_retraction;
m_extra_loading_move = config.extra_loading_move;
m_set_extruder_trimpot = config.high_current_on_filament_swap;
}
// Calculate where the priming lines should be - very naive test not detecting parallelograms or custom shapes
const std::vector<Vec2d>& bed_points = config.bed_shape.values;
m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed);
m_bed_width = BoundingBoxf(bed_points).size().x();
}
void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
{
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
m_filpar.push_back(FilamentParameters());
m_filpar[idx].material = config.filament_type.get_at(idx);
m_filpar[idx].temperature = config.temperature.get_at(idx);
m_filpar[idx].first_layer_temperature = config.first_layer_temperature.get_at(idx);
// If this is a single extruder MM printer, we will use all the SE-specific config values.
// Otherwise, the defaults will be used to turn off the SE stuff.
if (m_semm) {
m_filpar[idx].loading_speed = config.filament_loading_speed.get_at(idx);
m_filpar[idx].loading_speed_start = config.filament_loading_speed_start.get_at(idx);
m_filpar[idx].unloading_speed = config.filament_unloading_speed.get_at(idx);
m_filpar[idx].unloading_speed_start = config.filament_unloading_speed_start.get_at(idx);
m_filpar[idx].delay = config.filament_toolchange_delay.get_at(idx);
m_filpar[idx].cooling_moves = config.filament_cooling_moves.get_at(idx);
m_filpar[idx].cooling_initial_speed = config.filament_cooling_initial_speed.get_at(idx);
m_filpar[idx].cooling_final_speed = config.filament_cooling_final_speed.get_at(idx);
}
m_filpar[idx].filament_area = float((M_PI/4.f) * pow(config.filament_diameter.get_at(idx), 2)); // all extruders are assumed to have the same filament diameter at this point
float nozzle_diameter = config.nozzle_diameter.get_at(idx);
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
float max_vol_speed = config.filament_max_volumetric_speed.get_at(idx);
if (max_vol_speed!= 0.f)
m_filpar[idx].max_e_speed = (max_vol_speed / filament_area());
m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
if (m_semm) {
std::istringstream stream{config.filament_ramming_parameters.get_at(idx)};
float speed = 0.f;
stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
m_filpar[idx].ramming_line_width_multiplicator /= 100;
m_filpar[idx].ramming_step_multiplicator /= 100;
while (stream >> speed)
m_filpar[idx].ramming_speed.push_back(speed);
}
m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
}
// Returns gcode to prime the nozzles at the front edge of the print bed. // Returns gcode to prime the nozzles at the front edge of the print bed.
std::vector<WipeTower::ToolChangeResult> WipeTower::prime( std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
// print_z of the first layer. // print_z of the first layer.
@ -488,9 +571,11 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
// therefore the homing position is shifted inside the bed by 0.2 in the firmware to [0.2, -2.0]. // therefore the homing position is shifted inside the bed by 0.2 in the firmware to [0.2, -2.0].
// box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area); // box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
const float prime_section_width = std::min(240.f / tools.size(), 60.f); float prime_section_width = std::min(0.9f * m_bed_width / tools.size(), 60.f);
box_coordinates cleaning_box(Vec2f(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); box_coordinates cleaning_box(Vec2f(0.02f * m_bed_width, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
// In case of a circular bed, place it so it goes across the diameter and hope it will fit
if (m_bed_shape == CircularBed)
cleaning_box.translate(-m_bed_width/2 + m_bed_width * 0.03f, -m_bed_width * 0.12f);
std::vector<ToolChangeResult> results; std::vector<ToolChangeResult> results;
@ -818,6 +903,8 @@ void WipeTower::toolchange_Unload(
} }
} }
writer.disable_linear_advance();
// now the ramming itself: // now the ramming itself:
while (i < m_filpar[m_current_tool].ramming_speed.size()) while (i < m_filpar[m_current_tool].ramming_speed.size())
{ {

View file

@ -78,83 +78,12 @@ public:
// y -- y coordinates of wipe tower in mm ( left bottom corner ) // y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is ) // width -- width of wipe tower in mm ( default 60 mm - leave as it is )
// wipe_area -- space available for one toolchange in mm // wipe_area -- space available for one toolchange in mm
WipeTower(bool semm, float x, float y, float width, float rotation_angle, float cooling_tube_retraction, WipeTower(const PrintConfig& config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool);
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move,
float bridging, bool set_extruder_trimpot, GCodeFlavor flavor,
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
m_semm(semm),
m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_tower_rotation_angle(rotation_angle),
m_y_shift(0.f),
m_z_pos(0.f),
m_is_first_layer(false),
m_gcode_flavor(flavor),
m_bridging(bridging),
m_current_tool(initial_tool),
wipe_volumes(wiping_matrix)
{
// If this is a single extruder MM printer, we will use all the SE-specific config values.
// Otherwise, the defaults will be used to turn off the SE stuff.
if (m_semm) {
m_cooling_tube_retraction = cooling_tube_retraction;
m_cooling_tube_length = cooling_tube_length;
m_parking_pos_retraction = parking_pos_retraction;
m_extra_loading_move = extra_loading_move;
m_set_extruder_trimpot = set_extruder_trimpot;
}
}
virtual ~WipeTower() {} virtual ~WipeTower() {}
// Set the extruder properties. // Set the extruder properties.
void set_extruder(size_t idx, std::string material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start, void set_extruder(size_t idx, const PrintConfig& config);
float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float max_volumetric_speed,
float nozzle_diameter, float filament_diameter)
{
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
m_filpar.push_back(FilamentParameters());
m_filpar[idx].material = material;
m_filpar[idx].temperature = temp;
m_filpar[idx].first_layer_temperature = first_layer_temp;
// If this is a single extruder MM printer, we will use all the SE-specific config values.
// Otherwise, the defaults will be used to turn off the SE stuff.
if (m_semm) {
m_filpar[idx].loading_speed = loading_speed;
m_filpar[idx].loading_speed_start = loading_speed_start;
m_filpar[idx].unloading_speed = unloading_speed;
m_filpar[idx].unloading_speed_start = unloading_speed_start;
m_filpar[idx].delay = delay;
m_filpar[idx].cooling_moves = cooling_moves;
m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
m_filpar[idx].cooling_final_speed = cooling_final_speed;
}
m_filpar[idx].filament_area = float((M_PI/4.f) * pow(filament_diameter, 2)); // all extruders are assumed to have the same filament diameter at this point
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
if (max_volumetric_speed != 0.f)
m_filpar[idx].max_e_speed = (max_volumetric_speed / filament_area());
m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
if (m_semm) {
std::stringstream stream{ramming_parameters};
float speed = 0.f;
stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
m_filpar[idx].ramming_line_width_multiplicator /= 100;
m_filpar[idx].ramming_step_multiplicator /= 100;
while (stream >> speed)
m_filpar[idx].ramming_speed.push_back(speed);
}
m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
}
// Appends into internal structure m_plan containing info about the future wipe tower // Appends into internal structure m_plan containing info about the future wipe tower
// to be used before building begins. The entries must be added ordered in z. // to be used before building begins. The entries must be added ordered in z.
@ -263,7 +192,6 @@ private:
SHAPE_REVERSED = -1 SHAPE_REVERSED = -1
}; };
const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
const float WT_EPSILON = 1e-3f; const float WT_EPSILON = 1e-3f;
@ -295,6 +223,13 @@ private:
bool m_adhesion = true; bool m_adhesion = true;
GCodeFlavor m_gcode_flavor; GCodeFlavor m_gcode_flavor;
// Bed properties
enum {
RectangularBed,
CircularBed
} m_bed_shape;
float m_bed_width; // width of the bed bounding box
float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill. float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter. float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.

View file

@ -390,7 +390,7 @@ namespace Slic3r {
fclose(out); fclose(out);
in.close(); in.close();
if (rename_file(path_tmp, filename) != 0) if (rename_file(path_tmp, filename))
throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
"Is " + path_tmp + " locked?" + '\n'); "Is " + path_tmp + " locked?" + '\n');

View file

@ -111,6 +111,29 @@ inline bool segment_segment_intersection(const Vec2d &p1, const Vec2d &v1, const
return true; return true;
} }
inline int segments_could_intersect(
const Slic3r::Point &ip1, const Slic3r::Point &ip2,
const Slic3r::Point &jp1, const Slic3r::Point &jp2)
{
Vec2i64 iv = (ip2 - ip1).cast<int64_t>();
Vec2i64 vij1 = (jp1 - ip1).cast<int64_t>();
Vec2i64 vij2 = (jp2 - ip1).cast<int64_t>();
int64_t tij1 = cross2(iv, vij1);
int64_t tij2 = cross2(iv, vij2);
int sij1 = (tij1 > 0) ? 1 : ((tij1 < 0) ? -1 : 0); // signum
int sij2 = (tij2 > 0) ? 1 : ((tij2 < 0) ? -1 : 0);
return sij1 * sij2;
}
inline bool segments_intersect(
const Slic3r::Point &ip1, const Slic3r::Point &ip2,
const Slic3r::Point &jp1, const Slic3r::Point &jp2)
{
return segments_could_intersect(ip1, ip2, jp1, jp2) <= 0 &&
segments_could_intersect(jp1, jp2, ip1, ip2) <= 0;
}
Pointf3s convex_hull(Pointf3s points); Pointf3s convex_hull(Pointf3s points);
Polygon convex_hull(Points points); Polygon convex_hull(Points points);
Polygon convex_hull(const Polygons &polygons); Polygon convex_hull(const Polygons &polygons);

View file

@ -1,6 +1,11 @@
#include "MinAreaBoundingBox.hpp" #include "MinAreaBoundingBox.hpp"
#include <libslic3r/ExPolygon.hpp> #include <libslic3r/ExPolygon.hpp>
#if defined(_MSC_VER) && defined(__clang__)
#define BOOST_NO_CXX17_HDR_STRING_VIEW
#endif
#include <boost/rational.hpp> #include <boost/rational.hpp>
#include <libslic3r/Int128.hpp> #include <libslic3r/Int128.hpp>

View file

@ -0,0 +1,56 @@
#include "PolygonTrimmer.hpp"
#include "EdgeGrid.hpp"
#include "Geometry.hpp"
namespace Slic3r {
TrimmedLoop trim_loop(const Polygon &loop, const EdgeGrid::Grid &grid)
{
assert(! loop.empty());
assert(loop.size() >= 2);
TrimmedLoop out;
if (loop.size() >= 2) {
size_t cnt = loop.points.size();
struct Visitor {
Visitor(const EdgeGrid::Grid &grid, const Slic3r::Point *pt_prev, const Slic3r::Point *pt_this) : grid(grid), pt_prev(pt_prev), pt_this(pt_this) {}
void operator()(coord_t iy, coord_t ix) {
// Called with a row and colum of the grid cell, which is intersected by a line.
auto cell_data_range = grid.cell_data_range(iy, ix);
for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) {
// End points of the line segment and their vector.
auto segment = grid.segment(*it_contour_and_segment);
if (Geometry::segments_intersect(segment.first, segment.second, *pt_prev, *pt_this)) {
// The two segments intersect. Add them to the output.
}
}
}
const EdgeGrid::Grid &grid;
const Slic3r::Point *pt_this;
const Slic3r::Point *pt_prev;
} visitor(grid, &loop.points.back(), nullptr);
for (const Point &pt_this : loop.points) {
visitor.pt_this = &pt_this;
grid.visit_cells_intersecting_line(*visitor.pt_prev, pt_this, visitor);
visitor.pt_prev = &pt_this;
}
}
return out;
}
std::vector<TrimmedLoop> trim_loops(const Polygons &loops, const EdgeGrid::Grid &grid)
{
std::vector<TrimmedLoop> out;
out.reserve(loops.size());
for (const Polygon &loop : loops)
out.emplace_back(trim_loop(loop, grid));
return out;
}
}

View file

@ -0,0 +1,31 @@
#ifndef slic3r_PolygonTrimmer_hpp_
#define slic3r_PolygonTrimmer_hpp_
#include "libslic3r.h"
#include <vector>
#include <string>
#include "Line.hpp"
#include "MultiPoint.hpp"
#include "Polyline.hpp"
namespace Slic3r {
namespace EdgeGrid {
class Grid;
}
struct TrimmedLoop
{
std::vector<Point> points;
// Number of points per segment. Empty if the loop is
std::vector<unsigned int> segments;
bool is_trimmed() const { return ! segments.empty(); }
};
TrimmedLoop trim_loop(const Polygon &loop, const EdgeGrid::Grid &grid);
std::vector<TrimmedLoop> trim_loops(const Polygons &loops, const EdgeGrid::Grid &grid);
} // namespace Slic3r
#endif /* slic3r_PolygonTrimmer_hpp_ */

View file

@ -262,8 +262,14 @@ std::vector<unsigned int> Print::object_extruders() const
{ {
std::vector<unsigned int> extruders; std::vector<unsigned int> extruders;
extruders.reserve(m_regions.size() * 3); extruders.reserve(m_regions.size() * 3);
for (const PrintRegion *region : m_regions) std::vector<unsigned char> region_used(m_regions.size(), false);
region->collect_object_printing_extruders(extruders); for (const PrintObject *object : m_objects)
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes_per_region : object->region_volumes)
if (! volumes_per_region.empty())
region_used[&volumes_per_region - &object->region_volumes.front()] = true;
for (size_t idx_region = 0; idx_region < m_regions.size(); ++ idx_region)
if (region_used[idx_region])
m_regions[idx_region]->collect_object_printing_extruders(extruders);
sort_remove_duplicates(extruders); sort_remove_duplicates(extruders);
return extruders; return extruders;
} }
@ -273,17 +279,24 @@ std::vector<unsigned int> Print::support_material_extruders() const
{ {
std::vector<unsigned int> extruders; std::vector<unsigned int> extruders;
bool support_uses_current_extruder = false; bool support_uses_current_extruder = false;
auto num_extruders = (unsigned int)m_config.nozzle_diameter.size();
for (PrintObject *object : m_objects) { for (PrintObject *object : m_objects) {
if (object->has_support_material()) { if (object->has_support_material()) {
assert(object->config().support_material_extruder >= 0);
if (object->config().support_material_extruder == 0) if (object->config().support_material_extruder == 0)
support_uses_current_extruder = true; support_uses_current_extruder = true;
else else {
extruders.push_back(object->config().support_material_extruder - 1); unsigned int i = (unsigned int)object->config().support_material_extruder - 1;
extruders.emplace_back((i >= num_extruders) ? 0 : i);
}
assert(object->config().support_material_interface_extruder >= 0);
if (object->config().support_material_interface_extruder == 0) if (object->config().support_material_interface_extruder == 0)
support_uses_current_extruder = true; support_uses_current_extruder = true;
else else {
extruders.push_back(object->config().support_material_interface_extruder - 1); unsigned int i = (unsigned int)object->config().support_material_interface_extruder - 1;
extruders.emplace_back((i >= num_extruders) ? 0 : i);
}
} }
} }
@ -577,6 +590,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Apply variables to placeholder parser. The placeholder parser is used by G-code export, // Apply variables to placeholder parser. The placeholder parser is used by G-code export,
// which should be stopped if print_diff is not empty. // which should be stopped if print_diff is not empty.
size_t num_extruders = m_config.nozzle_diameter.size();
bool num_extruders_changed = false;
if (! full_config_diff.empty() || ! placeholder_parser_overrides.empty()) { if (! full_config_diff.empty() || ! placeholder_parser_overrides.empty()) {
update_apply_status(this->invalidate_step(psGCodeExport)); update_apply_status(this->invalidate_step(psGCodeExport));
m_placeholder_parser.apply_config(std::move(placeholder_parser_overrides)); m_placeholder_parser.apply_config(std::move(placeholder_parser_overrides));
@ -592,6 +607,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Handle changes to regions config defaults // Handle changes to regions config defaults
m_default_region_config.apply_only(new_full_config, region_diff, true); m_default_region_config.apply_only(new_full_config, region_diff, true);
m_full_print_config = std::move(new_full_config); m_full_print_config = std::move(new_full_config);
if (num_extruders != m_config.nozzle_diameter.size()) {
num_extruders = m_config.nozzle_diameter.size();
num_extruders_changed = true;
}
} }
class LayerRanges class LayerRanges
@ -768,7 +787,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
print_object_status.emplace(PrintObjectStatus(print_object)); print_object_status.emplace(PrintObjectStatus(print_object));
// 3) Synchronize ModelObjects & PrintObjects. // 3) Synchronize ModelObjects & PrintObjects.
size_t num_extruders = m_config.nozzle_diameter.size();
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) { for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
ModelObject &model_object = *m_model.objects[idx_model_object]; ModelObject &model_object = *m_model.objects[idx_model_object];
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id())); auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
@ -815,7 +833,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
bool object_config_changed = model_object.config != model_object_new.config; bool object_config_changed = model_object.config != model_object_new.config;
if (object_config_changed) if (object_config_changed)
static_cast<DynamicPrintConfig&>(model_object.config) = static_cast<const DynamicPrintConfig&>(model_object_new.config); static_cast<DynamicPrintConfig&>(model_object.config) = static_cast<const DynamicPrintConfig&>(model_object_new.config);
if (! object_diff.empty() || object_config_changed) { if (! object_diff.empty() || object_config_changed || num_extruders_changed) {
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders); PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) { for (auto it = range.first; it != range.second; ++ it) {
@ -1144,7 +1162,12 @@ std::string Print::validate() const
// #4043 // #4043
if (total_copies_count > 1 && ! m_config.complete_objects.value) if (total_copies_count > 1 && ! m_config.complete_objects.value)
return L("The Spiral Vase option can only be used when printing a single object."); return L("The Spiral Vase option can only be used when printing a single object.");
if (m_regions.size() > 1) assert(m_objects.size() == 1);
size_t num_regions = 0;
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes_per_region : m_objects.front()->region_volumes)
if (! volumes_per_region.empty())
++ num_regions;
if (num_regions > 1)
return L("The Spiral Vase option can only be used when printing single material objects."); return L("The Spiral Vase option can only be used when printing single material objects.");
} }
@ -1258,7 +1281,7 @@ std::string Print::validate() const
} else if (extrusion_width_min <= layer_height) { } else if (extrusion_width_min <= layer_height) {
err_msg = (boost::format(L("%1%=%2% mm is too low to be printable at a layer height %3% mm")) % opt_key % extrusion_width_min % layer_height).str(); err_msg = (boost::format(L("%1%=%2% mm is too low to be printable at a layer height %3% mm")) % opt_key % extrusion_width_min % layer_height).str();
return false; return false;
} else if (extrusion_width_max >= max_nozzle_diameter * 2.) { } else if (extrusion_width_max >= max_nozzle_diameter * 3.) {
err_msg = (boost::format(L("Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm")) % opt_key % extrusion_width_max % max_nozzle_diameter).str(); err_msg = (boost::format(L("Excessive %1%=%2% mm to be printable with a nozzle diameter %3% mm")) % opt_key % extrusion_width_max % max_nozzle_diameter).str();
return false; return false;
} }
@ -1762,15 +1785,7 @@ void Print::_make_wipe_tower()
this->throw_if_canceled(); this->throw_if_canceled();
// Initialize the wipe tower. // Initialize the wipe tower.
WipeTower wipe_tower( WipeTower wipe_tower(m_config, wipe_volumes, m_wipe_tower_data.tool_ordering.first_extruder());
m_config.single_extruder_multi_material.value,
float(m_config.wipe_tower_x.value), float(m_config.wipe_tower_y.value),
float(m_config.wipe_tower_width.value),
float(m_config.wipe_tower_rotation_angle.value), float(m_config.cooling_tube_retraction.value),
float(m_config.cooling_tube_length.value), float(m_config.parking_pos_retraction.value),
float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging),
m_config.high_current_on_filament_swap.value, m_config.gcode_flavor, wipe_volumes,
m_wipe_tower_data.tool_ordering.first_extruder());
//wipe_tower.set_retract(); //wipe_tower.set_retract();
//wipe_tower.set_zhop(); //wipe_tower.set_zhop();
@ -1779,22 +1794,7 @@ void Print::_make_wipe_tower()
for (size_t i = 0; i < number_of_extruders; ++ i) for (size_t i = 0; i < number_of_extruders; ++ i)
wipe_tower.set_extruder( wipe_tower.set_extruder(
i, i, m_config);
m_config.filament_type.get_at(i),
m_config.temperature.get_at(i),
m_config.first_layer_temperature.get_at(i),
(float)m_config.filament_loading_speed.get_at(i),
(float)m_config.filament_loading_speed_start.get_at(i),
(float)m_config.filament_unloading_speed.get_at(i),
(float)m_config.filament_unloading_speed_start.get_at(i),
(float)m_config.filament_toolchange_delay.get_at(i),
m_config.filament_cooling_moves.get_at(i),
(float)m_config.filament_cooling_initial_speed.get_at(i),
(float)m_config.filament_cooling_final_speed.get_at(i),
m_config.filament_ramming_parameters.get_at(i),
(float)m_config.filament_max_volumetric_speed.get_at(i),
(float)m_config.nozzle_diameter.get_at(i),
(float)m_config.filament_diameter.get_at(i));
m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>( m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false)); wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));

View file

@ -2412,6 +2412,22 @@ void PrintConfigDef::init_sla_params()
def->mode = comExpert; def->mode = comExpert;
def->set_default_value(new ConfigOptionInt(10)); def->set_default_value(new ConfigOptionInt(10));
def = this->add("min_exposure_time", coFloat);
def->label = L("Minimum exposure time");
def->tooltip = L("Minimum exposure time");
def->sidetext = L("s");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("max_exposure_time", coFloat);
def->label = L("Maximum exposure time");
def->tooltip = L("Maximum exposure time");
def->sidetext = L("s");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(100));
def = this->add("exposure_time", coFloat); def = this->add("exposure_time", coFloat);
def->label = L("Exposure time"); def->label = L("Exposure time");
def->tooltip = L("Exposure time"); def->tooltip = L("Exposure time");
@ -2419,6 +2435,22 @@ void PrintConfigDef::init_sla_params()
def->min = 0; def->min = 0;
def->set_default_value(new ConfigOptionFloat(10)); def->set_default_value(new ConfigOptionFloat(10));
def = this->add("min_initial_exposure_time", coFloat);
def->label = L("Minimum initial exposure time");
def->tooltip = L("Minimum initial exposure time");
def->sidetext = L("s");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("max_initial_exposure_time", coFloat);
def->label = L("Maximum initial exposure time");
def->tooltip = L("Maximum initial exposure time");
def->sidetext = L("s");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(150));
def = this->add("initial_exposure_time", coFloat); def = this->add("initial_exposure_time", coFloat);
def->label = L("Initial exposure time"); def->label = L("Initial exposure time");
def->tooltip = L("Initial exposure time"); def->tooltip = L("Initial exposure time");
@ -3290,7 +3322,7 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->tooltip = L("Messages with severity lower or eqal to the loglevel will be printed out. 0:trace, 1:debug, 2:info, 3:warning, 4:error, 5:fatal"); def->tooltip = L("Messages with severity lower or eqal to the loglevel will be printed out. 0:trace, 1:debug, 2:info, 3:warning, 4:error, 5:fatal");
def->min = 0; def->min = 0;
#if defined(_MSC_VER) && defined(SLIC3R_GUI) #if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(SLIC3R_GUI)
def = this->add("sw_renderer", coBool); def = this->add("sw_renderer", coBool);
def->label = L("Render with a software renderer"); def->label = L("Render with a software renderer");
def->tooltip = L("Render with a software renderer. The bundled MESA software renderer is loaded instead of the default OpenGL driver."); def->tooltip = L("Render with a software renderer. The bundled MESA software renderer is loaded instead of the default OpenGL driver.");

View file

@ -1131,6 +1131,10 @@ public:
ConfigOptionFloat fast_tilt_time; ConfigOptionFloat fast_tilt_time;
ConfigOptionFloat slow_tilt_time; ConfigOptionFloat slow_tilt_time;
ConfigOptionFloat area_fill; ConfigOptionFloat area_fill;
ConfigOptionFloat min_exposure_time;
ConfigOptionFloat max_exposure_time;
ConfigOptionFloat min_initial_exposure_time;
ConfigOptionFloat max_initial_exposure_time;
protected: protected:
void initialize(StaticCacheBase &cache, const char *base_ptr) void initialize(StaticCacheBase &cache, const char *base_ptr)
{ {
@ -1150,6 +1154,10 @@ protected:
OPT_PTR(fast_tilt_time); OPT_PTR(fast_tilt_time);
OPT_PTR(slow_tilt_time); OPT_PTR(slow_tilt_time);
OPT_PTR(area_fill); OPT_PTR(area_fill);
OPT_PTR(min_exposure_time);
OPT_PTR(max_exposure_time);
OPT_PTR(min_initial_exposure_time);
OPT_PTR(max_initial_exposure_time);
} }
}; };

View file

@ -161,7 +161,7 @@ void PrintObject::make_perimeters()
const PrintRegion &region = *m_print->regions()[region_id]; const PrintRegion &region = *m_print->regions()[region_id];
if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2) if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
continue; continue;
BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start"; BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start";
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size() - 1), tbb::blocked_range<size_t>(0, m_layers.size() - 1),
@ -2379,7 +2379,7 @@ void PrintObject::discover_horizontal_shells()
if (new_internal_solid.empty()) { if (new_internal_solid.empty()) {
// No internal solid needed on this layer. In order to decide whether to continue // No internal solid needed on this layer. In order to decide whether to continue
// searching on the next neighbor (thus enforcing the configured number of solid // searching on the next neighbor (thus enforcing the configured number of solid
// layers, use different strategies according to configured infill density: // layers, use different strategies according to configured infill density:
if (region_config.fill_density.value == 0) { if (region_config.fill_density.value == 0) {
// If user expects the object to be void (for example a hollow sloping vase), // If user expects the object to be void (for example a hollow sloping vase),
// don't continue the search. In this case, we only generate the external solid // don't continue the search. In this case, we only generate the external solid

View file

@ -46,7 +46,7 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir
} }
double nozzle_diameter = m_print->config().nozzle_diameter.get_at(extruder-1); double nozzle_diameter = m_print->config().nozzle_diameter.get_at(extruder-1);
return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0); return Flow::new_from_config_width(role, config_width, (float)nozzle_diameter, (float)layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0f);
} }
coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
@ -64,16 +64,27 @@ coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, std::vector<unsigned int> &object_extruders) void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, std::vector<unsigned int> &object_extruders)
{ {
// These checks reflect the same logic used in the GUI for enabling/disabling extruder selection fields. // These checks reflect the same logic used in the GUI for enabling/disabling extruder selection fields.
auto num_extruders = (int)print_config.nozzle_diameter.size();
auto emplace_extruder = [num_extruders, &object_extruders](int extruder_id) {
int i = std::max(0, extruder_id - 1);
object_extruders.emplace_back((i >= num_extruders) ? 0 : i);
};
if (region_config.perimeters.value > 0 || print_config.brim_width.value > 0) if (region_config.perimeters.value > 0 || print_config.brim_width.value > 0)
object_extruders.emplace_back(region_config.perimeter_extruder - 1); emplace_extruder(region_config.perimeter_extruder);
if (region_config.fill_density.value > 0) if (region_config.fill_density.value > 0)
object_extruders.emplace_back(region_config.infill_extruder - 1); emplace_extruder(region_config.infill_extruder);
if (region_config.top_solid_layers.value > 0 || region_config.bottom_solid_layers.value > 0) if (region_config.top_solid_layers.value > 0 || region_config.bottom_solid_layers.value > 0)
object_extruders.emplace_back(region_config.solid_infill_extruder - 1); emplace_extruder(region_config.solid_infill_extruder);
} }
void PrintRegion::collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const void PrintRegion::collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const
{ {
auto num_extruders = (int)print()->config().nozzle_diameter.size();
// PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder.
// If not, then there must be something wrong with the Print::apply() function.
assert(this->config().perimeter_extruder <= num_extruders);
assert(this->config().infill_extruder <= num_extruders);
assert(this->config().solid_infill_extruder <= num_extruders);
collect_object_printing_extruders(print()->config(), this->config(), object_extruders); collect_object_printing_extruders(print()->config(), this->config(), object_extruders);
} }

View file

@ -1,5 +1,5 @@
#include "igl/random_points_on_mesh.h" //#include "igl/random_points_on_mesh.h"
#include "igl/AABB.h" //#include "igl/AABB.h"
#include <tbb/parallel_for.h> #include <tbb/parallel_for.h>
@ -101,7 +101,7 @@ static std::vector<SLAAutoSupports::MyLayer> make_layers(
std::vector<SLAAutoSupports::MyLayer> layers; std::vector<SLAAutoSupports::MyLayer> layers;
layers.reserve(slices.size()); layers.reserve(slices.size());
for (size_t i = 0; i < slices.size(); ++ i) for (size_t i = 0; i < slices.size(); ++ i)
layers.emplace_back(i, heights[i]); layers.emplace_back(i, heights[i]);
// FIXME: calculate actual pixel area from printer config: // FIXME: calculate actual pixel area from printer config:
//const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option<ConfigOptionFloat>("display_width") / wxGetApp().preset_bundle->project_config.option<ConfigOptionInt>("display_pixels_x"), 2.f); // //const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option<ConfigOptionFloat>("display_width") / wxGetApp().preset_bundle->project_config.option<ConfigOptionInt>("display_pixels_x"), 2.f); //
@ -114,47 +114,47 @@ static std::vector<SLAAutoSupports::MyLayer> make_layers(
if ((layer_id % 8) == 0) if ((layer_id % 8) == 0)
// Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves.
throw_on_cancel(); throw_on_cancel();
SLAAutoSupports::MyLayer &layer = layers[layer_id]; SLAAutoSupports::MyLayer &layer = layers[layer_id];
const ExPolygons &islands = slices[layer_id]; const ExPolygons &islands = slices[layer_id];
//FIXME WTF? //FIXME WTF?
const float height = (layer_id>2 ? heights[layer_id-3] : heights[0]-(heights[1]-heights[0])); const float height = (layer_id>2 ? heights[layer_id-3] : heights[0]-(heights[1]-heights[0]));
layer.islands.reserve(islands.size()); layer.islands.reserve(islands.size());
for (const ExPolygon &island : islands) { for (const ExPolygon &island : islands) {
float area = float(island.area() * SCALING_FACTOR * SCALING_FACTOR); float area = float(island.area() * SCALING_FACTOR * SCALING_FACTOR);
if (area >= pixel_area) if (area >= pixel_area)
//FIXME this is not a correct centroid of a polygon with holes. //FIXME this is not a correct centroid of a polygon with holes.
layer.islands.emplace_back(layer, island, get_extents(island.contour), Slic3r::unscale(island.contour.centroid()).cast<float>(), area, height); layer.islands.emplace_back(layer, island, get_extents(island.contour), Slic3r::unscale(island.contour.centroid()).cast<float>(), area, height);
} }
} }
}); });
// Calculate overlap of successive layers. Link overlapping islands. // Calculate overlap of successive layers. Link overlapping islands.
tbb::parallel_for(tbb::blocked_range<size_t>(1, layers.size(), 8), tbb::parallel_for(tbb::blocked_range<size_t>(1, layers.size(), 8),
[&layers, &heights, throw_on_cancel](const tbb::blocked_range<size_t>& range) { [&layers, &heights, throw_on_cancel](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) {
if ((layer_id % 2) == 0) if ((layer_id % 2) == 0)
// Don't call the following function too often as it flushes CPU write caches due to synchronization primitves. // Don't call the following function too often as it flushes CPU write caches due to synchronization primitves.
throw_on_cancel(); throw_on_cancel();
SLAAutoSupports::MyLayer &layer_above = layers[layer_id]; SLAAutoSupports::MyLayer &layer_above = layers[layer_id];
SLAAutoSupports::MyLayer &layer_below = layers[layer_id - 1]; SLAAutoSupports::MyLayer &layer_below = layers[layer_id - 1];
//FIXME WTF? //FIXME WTF?
const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]); const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]);
const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports
const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle))); const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle)));
const float slope_angle = 75.f * (float(M_PI)/180.f); // smaller number - less supports const float slope_angle = 75.f * (float(M_PI)/180.f); // smaller number - less supports
const float slope_offset = float(scale_(layer_height / std::tan(slope_angle))); const float slope_offset = float(scale_(layer_height / std::tan(slope_angle)));
//FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands. //FIXME This has a quadratic time complexity, it will be excessively slow for many tiny islands.
for (SLAAutoSupports::Structure &top : layer_above.islands) { for (SLAAutoSupports::Structure &top : layer_above.islands) {
for (SLAAutoSupports::Structure &bottom : layer_below.islands) { for (SLAAutoSupports::Structure &bottom : layer_below.islands) {
float overlap_area = top.overlap_area(bottom); float overlap_area = top.overlap_area(bottom);
if (overlap_area > 0) { if (overlap_area > 0) {
top.islands_below.emplace_back(&bottom, overlap_area); top.islands_below.emplace_back(&bottom, overlap_area);
bottom.islands_above.emplace_back(&top, overlap_area); bottom.islands_above.emplace_back(&top, overlap_area);
} }
} }
if (! top.islands_below.empty()) { if (! top.islands_below.empty()) {
Polygons top_polygons = to_polygons(*top.polygon); Polygons top_polygons = to_polygons(*top.polygon);
Polygons bottom_polygons = top.polygons_below(); Polygons bottom_polygons = top.polygons_below();
top.overhangs = diff_ex(top_polygons, bottom_polygons); top.overhangs = diff_ex(top_polygons, bottom_polygons);
if (! top.overhangs.empty()) { if (! top.overhangs.empty()) {
top.overhangs_area = 0.f; top.overhangs_area = 0.f;
@ -164,21 +164,21 @@ static std::vector<SLAAutoSupports::MyLayer> make_layers(
expolys_with_areas.emplace_back(&ex, area); expolys_with_areas.emplace_back(&ex, area);
top.overhangs_area += area; top.overhangs_area += area;
} }
std::sort(expolys_with_areas.begin(), expolys_with_areas.end(), std::sort(expolys_with_areas.begin(), expolys_with_areas.end(),
[](const std::pair<ExPolygon*, float> &p1, const std::pair<ExPolygon*, float> &p2) [](const std::pair<ExPolygon*, float> &p1, const std::pair<ExPolygon*, float> &p2)
{ return p1.second > p2.second; }); { return p1.second > p2.second; });
ExPolygons overhangs_sorted; ExPolygons overhangs_sorted;
for (auto &p : expolys_with_areas) for (auto &p : expolys_with_areas)
overhangs_sorted.emplace_back(std::move(*p.first)); overhangs_sorted.emplace_back(std::move(*p.first));
top.overhangs = std::move(overhangs_sorted); top.overhangs = std::move(overhangs_sorted);
top.overhangs_area *= float(SCALING_FACTOR * SCALING_FACTOR); top.overhangs_area *= float(SCALING_FACTOR * SCALING_FACTOR);
top.overhangs_slopes = diff_ex(top_polygons, offset(bottom_polygons, slope_offset)); top.overhangs_slopes = diff_ex(top_polygons, offset(bottom_polygons, slope_offset));
top.dangling_areas = diff_ex(top_polygons, offset(bottom_polygons, between_layers_offset)); top.dangling_areas = diff_ex(top_polygons, offset(bottom_polygons, between_layers_offset));
} }
} }
} }
} }
}); });
return layers; return layers;
} }
@ -207,14 +207,14 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::
support_force_bottom[i] = layer_bottom->islands[i].supports_force_total(); support_force_bottom[i] = layer_bottom->islands[i].supports_force_total();
} }
for (Structure &top : layer_top->islands) for (Structure &top : layer_top->islands)
for (Structure::Link &bottom_link : top.islands_below) { for (Structure::Link &bottom_link : top.islands_below) {
Structure &bottom = *bottom_link.island; Structure &bottom = *bottom_link.island;
//float centroids_dist = (bottom.centroid - top.centroid).norm(); //float centroids_dist = (bottom.centroid - top.centroid).norm();
// Penalization resulting from centroid offset: // Penalization resulting from centroid offset:
// bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area)); // bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area));
float &support_force = support_force_bottom[&bottom - layer_bottom->islands.data()]; float &support_force = support_force_bottom[&bottom - layer_bottom->islands.data()];
//FIXME this condition does not reflect a bifurcation into a one large island and one tiny island well, it incorrectly resets the support force to zero. //FIXME this condition does not reflect a bifurcation into a one large island and one tiny island well, it incorrectly resets the support force to zero.
// One should rather work with the overlap area vs overhang area. // One should rather work with the overlap area vs overhang area.
// support_force *= std::min(1.f, 1.f - std::min(1.f, 0.1f * centroids_dist * centroids_dist / bottom.area)); // support_force *= std::min(1.f, 1.f - std::min(1.f, 0.1f * centroids_dist * centroids_dist / bottom.area));
// Penalization resulting from increasing polygon area: // Penalization resulting from increasing polygon area:
support_force *= std::min(1.f, 20.f * bottom.area / top.area); support_force *= std::min(1.f, 20.f * bottom.area / top.area);
@ -224,10 +224,10 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::
for (Structure &below : layer_bottom->islands) { for (Structure &below : layer_bottom->islands) {
float below_support_force = support_force_bottom[&below - layer_bottom->islands.data()]; float below_support_force = support_force_bottom[&below - layer_bottom->islands.data()];
float above_overlap_area = 0.f; float above_overlap_area = 0.f;
for (Structure::Link &above_link : below.islands_above) for (Structure::Link &above_link : below.islands_above)
above_overlap_area += above_link.overlap_area; above_overlap_area += above_link.overlap_area;
for (Structure::Link &above_link : below.islands_above) for (Structure::Link &above_link : below.islands_above)
above_link.island->supports_force_inherited += below_support_force * above_link.overlap_area / above_overlap_area; above_link.island->supports_force_inherited += below_support_force * above_link.overlap_area / above_overlap_area;
} }
} }
// Now iterate over all polygons and append new points if needed. // Now iterate over all polygons and append new points if needed.
@ -266,7 +266,7 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::
} }
std::vector<Vec2f> sample_expolygon(const ExPolygon &expoly, float samples_per_mm2, std::mt19937 &rng) std::vector<Vec2f> sample_expolygon(const ExPolygon &expoly, float samples_per_mm2, std::mt19937 &rng)
{ {
// Triangulate the polygon with holes into triplets of 3D points. // Triangulate the polygon with holes into triplets of 3D points.
std::vector<Vec2f> triangles = Slic3r::triangulate_expolygon_2f(expoly); std::vector<Vec2f> triangles = Slic3r::triangulate_expolygon_2f(expoly);
@ -347,7 +347,7 @@ static inline std::vector<Vec2f> poisson_disk_from_samples(const std::vector<Vec
sample.cell_id = ((pt - corner_min) / radius).cast<int>(); sample.cell_id = ((pt - corner_min) / radius).cast<int>();
raw_samples_sorted.emplace_back(sample); raw_samples_sorted.emplace_back(sample);
} }
std::sort(raw_samples_sorted.begin(), raw_samples_sorted.end(), [](const RawSample &lhs, const RawSample &rhs) std::sort(raw_samples_sorted.begin(), raw_samples_sorted.end(), [](const RawSample &lhs, const RawSample &rhs)
{ return lhs.cell_id.x() < rhs.cell_id.x() || (lhs.cell_id.x() == rhs.cell_id.x() && lhs.cell_id.y() < rhs.cell_id.y()); }); { return lhs.cell_id.x() < rhs.cell_id.x() || (lhs.cell_id.x() == rhs.cell_id.x() && lhs.cell_id.y() < rhs.cell_id.y()); });
struct PoissonDiskGridEntry { struct PoissonDiskGridEntry {
@ -464,10 +464,10 @@ void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& stru
//FIXME share the random generator. The random generator may be not so cheap to initialize, also we don't want the random generator to be restarted for each polygon. //FIXME share the random generator. The random generator may be not so cheap to initialize, also we don't want the random generator to be restarted for each polygon.
std::random_device rd; std::random_device rd;
std::mt19937 rng(rd()); std::mt19937 rng(rd());
std::vector<Vec2f> raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, rng); std::vector<Vec2f> raw_samples = sample_expolygon_with_boundary(islands, samples_per_mm2, 5.f / poisson_radius, rng);
std::vector<Vec2f> poisson_samples; std::vector<Vec2f> poisson_samples;
for (size_t iter = 0; iter < 4; ++ iter) { for (size_t iter = 0; iter < 4; ++ iter) {
poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius, poisson_samples = poisson_disk_from_samples(raw_samples, poisson_radius,
[&structure, &grid3d, min_spacing](const Vec2f &pos) { [&structure, &grid3d, min_spacing](const Vec2f &pos) {
return grid3d.collides_with(pos, &structure, min_spacing); return grid3d.collides_with(pos, &structure, min_spacing);
}); });
@ -481,21 +481,21 @@ void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& stru
} }
#ifdef SLA_AUTOSUPPORTS_DEBUG #ifdef SLA_AUTOSUPPORTS_DEBUG
{ {
static int irun = 0; static int irun = 0;
Slic3r::SVG svg(debug_out_path("SLA_supports-uniformly_cover-%d.svg", irun ++), get_extents(islands)); Slic3r::SVG svg(debug_out_path("SLA_supports-uniformly_cover-%d.svg", irun ++), get_extents(islands));
for (const ExPolygon &island : islands) for (const ExPolygon &island : islands)
svg.draw(island); svg.draw(island);
for (const Vec2f &pt : raw_samples) for (const Vec2f &pt : raw_samples)
svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "red"); svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "red");
for (const Vec2f &pt : poisson_samples) for (const Vec2f &pt : poisson_samples)
svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "blue"); svg.draw(Point(scale_(pt.x()), scale_(pt.y())), "blue");
} }
#endif /* NDEBUG */ #endif /* NDEBUG */
// assert(! poisson_samples.empty()); // assert(! poisson_samples.empty());
if (poisson_samples_target < poisson_samples.size()) { if (poisson_samples_target < poisson_samples.size()) {
std::shuffle(poisson_samples.begin(), poisson_samples.end(), rng); std::shuffle(poisson_samples.begin(), poisson_samples.end(), rng);
poisson_samples.erase(poisson_samples.begin() + poisson_samples_target, poisson_samples.end()); poisson_samples.erase(poisson_samples.begin() + poisson_samples_target, poisson_samples.end());
} }
for (const Vec2f &pt : poisson_samples) { for (const Vec2f &pt : poisson_samples) {

View file

@ -85,7 +85,7 @@ using Portion = std::tuple<double, double>;
// Set this to true to enable full parallelism in this module. // Set this to true to enable full parallelism in this module.
// Only the well tested parts will be concurrent if this is set to false. // Only the well tested parts will be concurrent if this is set to false.
const constexpr bool USE_FULL_CONCURRENCY = false; const constexpr bool USE_FULL_CONCURRENCY = true;
template<bool> struct _ccr {}; template<bool> struct _ccr {};
@ -1194,7 +1194,7 @@ class SLASupportTree::Algorithm {
// Now a and b vectors are perpendicular to v and to each other. // Now a and b vectors are perpendicular to v and to each other.
// Together they define the plane where we have to iterate with the // Together they define the plane where we have to iterate with the
// given angles in the 'phis' vector // given angles in the 'phis' vector
ccr_par::enumerate(phis.begin(), phis.end(), ccr_seq::enumerate(phis.begin(), phis.end(),
[&hits, &m, sd, r_pin, r_back, s, a, b, c] [&hits, &m, sd, r_pin, r_back, s, a, b, c]
(double phi, size_t i) (double phi, size_t i)
{ {
@ -1297,7 +1297,7 @@ class SLASupportTree::Algorithm {
// Hit results // Hit results
std::array<HitResult, SAMPLES> hits; std::array<HitResult, SAMPLES> hits;
ccr_par::enumerate(phis.begin(), phis.end(), ccr_seq::enumerate(phis.begin(), phis.end(),
[&m, a, b, sd, dir, r, s, ins_check, &hits] [&m, a, b, sd, dir, r, s, ins_check, &hits]
(double phi, size_t i) (double phi, size_t i)
{ {
@ -2588,7 +2588,7 @@ SLASupportTree::SLASupportTree(double gnd_lvl): m_impl(new Impl()) {
const TriangleMesh &SLASupportTree::merged_mesh() const const TriangleMesh &SLASupportTree::merged_mesh() const
{ {
return get().merged_mesh(); return m_impl->merged_mesh();
} }
void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const { void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const {

View file

@ -148,9 +148,9 @@ std::vector<BoxIndexEl> BoxIndex::query(const BoundingBox &qrbb,
BoxIndex::QueryType qt) BoxIndex::QueryType qt)
{ {
namespace bgi = boost::geometry::index; namespace bgi = boost::geometry::index;
std::vector<BoxIndexEl> ret; ret.reserve(m_impl->m_store.size()); std::vector<BoxIndexEl> ret; ret.reserve(m_impl->m_store.size());
switch (qt) { switch (qt) {
case qtIntersects: case qtIntersects:
m_impl->m_store.query(bgi::intersects(qrbb), std::back_inserter(ret)); m_impl->m_store.query(bgi::intersects(qrbb), std::back_inserter(ret));
@ -158,7 +158,7 @@ std::vector<BoxIndexEl> BoxIndex::query(const BoundingBox &qrbb,
case qtWithin: case qtWithin:
m_impl->m_store.query(bgi::within(qrbb), std::back_inserter(ret)); m_impl->m_store.query(bgi::within(qrbb), std::back_inserter(ret));
} }
return ret; return ret;
} }
@ -198,9 +198,9 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) {
F.resize(stl.stats.number_of_facets, 3); F.resize(stl.stats.number_of_facets, 3);
for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) { for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) {
const stl_facet &facet = stl.facet_start[i]; const stl_facet &facet = stl.facet_start[i];
V.block<1, 3>(3 * i + 0, 0) = facet.vertex[0].cast<double>(); V.block<1, 3>(3 * i + 0, 0) = facet.vertex[0].cast<double>();
V.block<1, 3>(3 * i + 1, 0) = facet.vertex[1].cast<double>(); V.block<1, 3>(3 * i + 1, 0) = facet.vertex[1].cast<double>();
V.block<1, 3>(3 * i + 2, 0) = facet.vertex[2].cast<double>(); V.block<1, 3>(3 * i + 2, 0) = facet.vertex[2].cast<double>();
F(i, 0) = int(3*i+0); F(i, 0) = int(3*i+0);
F(i, 1) = int(3*i+1); F(i, 1) = int(3*i+1);
F(i, 2) = int(3*i+2); F(i, 2) = int(3*i+2);
@ -306,6 +306,7 @@ PointSet normals(const PointSet& points,
PointSet ret(range.size(), 3); PointSet ret(range.size(), 3);
// for (size_t ridx = 0; ridx < range.size(); ++ridx)
tbb::parallel_for(size_t(0), range.size(), tbb::parallel_for(size_t(0), range.size(),
[&ret, &range, &mesh, &points, thr, eps](size_t ridx) [&ret, &range, &mesh, &points, thr, eps](size_t ridx)
{ {

View file

@ -16,6 +16,12 @@
// For geometry algorithms with native Clipper types (no copies and conversions) // For geometry algorithms with native Clipper types (no copies and conversions)
#include <libnest2d/backends/clipper/geometries.hpp> #include <libnest2d/backends/clipper/geometries.hpp>
// #define SLAPRINT_DO_BENCHMARK
#ifdef SLAPRINT_DO_BENCHMARK
#include <libnest2d/tools/benchmark.h>
#endif
//#include <tbb/spin_mutex.h>//#include "tbb/mutex.h" //#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
#include "I18N.hpp" #include "I18N.hpp"
@ -686,6 +692,20 @@ std::string SLAPrint::validate() const
} }
} }
double expt_max = m_printer_config.max_exposure_time.getFloat();
double expt_min = m_printer_config.min_exposure_time.getFloat();
double expt_cur = m_material_config.exposure_time.getFloat();
if (expt_cur < expt_min || expt_cur > expt_max)
return L("Exposition time is out of printer profile bounds.");
double iexpt_max = m_printer_config.max_initial_exposure_time.getFloat();
double iexpt_min = m_printer_config.min_initial_exposure_time.getFloat();
double iexpt_cur = m_material_config.initial_exposure_time.getFloat();
if (iexpt_cur < iexpt_min || iexpt_cur > iexpt_max)
return L("Initial exposition time is out of printer profile bounds.");
return ""; return "";
} }
@ -1441,7 +1461,7 @@ void SLAPrint::process()
if(canceled()) return; if(canceled()) return;
// Sequential version (for testing) // Sequential version (for testing)
// for(unsigned l = 0; l < lvlcnt; ++l) process_level(l); // for(unsigned l = 0; l < lvlcnt; ++l) lvlfn(l);
// Print all the layers in parallel // Print all the layers in parallel
tbb::parallel_for<unsigned, decltype(lvlfn)>(0, lvlcnt, lvlfn); tbb::parallel_for<unsigned, decltype(lvlfn)>(0, lvlcnt, lvlfn);
@ -1458,44 +1478,45 @@ void SLAPrint::process()
using slaposFn = std::function<void(SLAPrintObject&)>; using slaposFn = std::function<void(SLAPrintObject&)>;
using slapsFn = std::function<void(void)>; using slapsFn = std::function<void(void)>;
std::array<slaposFn, slaposCount> pobj_program = slaposFn pobj_program[] =
{ {
slice_model, slice_model, support_points, support_tree, base_pool, slice_supports
support_points,
support_tree,
base_pool,
slice_supports
}; };
std::array<slapsFn, slapsCount> print_program = // We want to first process all objects...
{ std::vector<SLAPrintObjectStep> level1_obj_steps = {
merge_slices_and_eval_stats, slaposObjectSlice, slaposSupportPoints, slaposSupportTree, slaposBasePool
rasterize
}; };
// and then slice all supports to allow preview to be displayed ASAP
std::vector<SLAPrintObjectStep> level2_obj_steps = {
slaposSliceSupports
};
slapsFn print_program[] = { merge_slices_and_eval_stats, rasterize };
SLAPrintStep print_steps[] = { slapsMergeSlicesAndEval, slapsRasterize };
double st = min_objstatus; double st = min_objstatus;
unsigned incr = 0;
BOOST_LOG_TRIVIAL(info) << "Start slicing process."; BOOST_LOG_TRIVIAL(info) << "Start slicing process.";
// TODO: this loop could run in parallel but should not exhaust all the CPU #ifdef SLAPRINT_DO_BENCHMARK
// power available Benchmark bench;
// Calculate the support structures first before slicing the supports, #else
// so that the preview will get displayed ASAP for all objects. struct {
std::vector<SLAPrintObjectStep> step_ranges = {slaposObjectSlice, void start() {} void stop() {} double getElapsedSec() { return .0; }
slaposSliceSupports, } bench;
slaposCount}; #endif
for (size_t idx_range = 0; idx_range + 1 < step_ranges.size(); ++idx_range) { std::array<double, slaposCount + slapsCount> step_times {};
auto apply_steps_on_objects =
[this, &st, ostepd, &pobj_program, &step_times, &bench]
(const std::vector<SLAPrintObjectStep> &steps)
{
unsigned incr = 0;
for (SLAPrintObject *po : m_objects) { for (SLAPrintObject *po : m_objects) {
for (SLAPrintObjectStep step : steps) {
BOOST_LOG_TRIVIAL(info)
<< "Slicing object " << po->model_object()->name;
for (int s = int(step_ranges[idx_range]);
s < int(step_ranges[idx_range + 1]);
++s) {
auto currentstep = static_cast<SLAPrintObjectStep>(s);
// Cancellation checking. Each step will check for // Cancellation checking. Each step will check for
// cancellation on its own and return earlier gracefully. // cancellation on its own and return earlier gracefully.
@ -1505,39 +1526,38 @@ void SLAPrint::process()
st += incr * ostepd; st += incr * ostepd;
if (po->m_stepmask[currentstep] if (po->m_stepmask[step] && po->set_started(step)) {
&& po->set_started(currentstep)) { m_report_status(*this, st, OBJ_STEP_LABELS(step));
m_report_status(*this, bench.start();
st, pobj_program[step](*po);
OBJ_STEP_LABELS(currentstep)); bench.stop();
pobj_program[currentstep](*po); step_times[step] += bench.getElapsedSec();
throw_if_canceled(); throw_if_canceled();
po->set_done(currentstep); po->set_done(step);
} }
incr = OBJ_STEP_LEVELS[currentstep]; incr = OBJ_STEP_LEVELS[step];
} }
} }
}
std::array<SLAPrintStep, slapsCount> printsteps = {
slapsMergeSlicesAndEval, slapsRasterize
}; };
apply_steps_on_objects(level1_obj_steps);
apply_steps_on_objects(level2_obj_steps);
// this would disable the rasterization step // this would disable the rasterization step
// m_stepmask[slapsRasterize] = false; // std::fill(m_stepmask.begin(), m_stepmask.end(), false);
double pstd = (100 - max_objstatus) / 100.0; double pstd = (100 - max_objstatus) / 100.0;
st = max_objstatus; st = max_objstatus;
for(size_t s = 0; s < print_program.size(); ++s) { for(SLAPrintStep currentstep : print_steps) {
auto currentstep = printsteps[s];
throw_if_canceled(); throw_if_canceled();
if(m_stepmask[currentstep] && set_started(currentstep)) if (m_stepmask[currentstep] && set_started(currentstep)) {
{
m_report_status(*this, st, PRINT_STEP_LABELS(currentstep)); m_report_status(*this, st, PRINT_STEP_LABELS(currentstep));
bench.start();
print_program[currentstep](); print_program[currentstep]();
bench.stop();
step_times[slaposCount + currentstep] += bench.getElapsedSec();
throw_if_canceled(); throw_if_canceled();
set_done(currentstep); set_done(currentstep);
} }
@ -1547,6 +1567,21 @@ void SLAPrint::process()
// If everything vent well // If everything vent well
m_report_status(*this, 100, L("Slicing done")); m_report_status(*this, 100, L("Slicing done"));
#ifdef SLAPRINT_DO_BENCHMARK
std::string csvbenchstr;
for (size_t i = 0; i < size_t(slaposCount); ++i)
csvbenchstr += OBJ_STEP_LABELS(i) + ";";
for (size_t i = 0; i < size_t(slapsCount); ++i)
csvbenchstr += PRINT_STEP_LABELS(i) + ";";
csvbenchstr += "\n";
for (double t : step_times) csvbenchstr += std::to_string(t) + ";";
std::cout << "Performance stats: \n" << csvbenchstr << std::endl;
#endif
} }
bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys, bool &invalidate_all_model_objects) bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys, bool &invalidate_all_model_objects)
@ -1565,7 +1600,11 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
// Cache the plenty of parameters, which influence the final rasterization only, // Cache the plenty of parameters, which influence the final rasterization only,
// or they are only notes not influencing the rasterization step. // or they are only notes not influencing the rasterization step.
static std::unordered_set<std::string> steps_rasterize = { static std::unordered_set<std::string> steps_rasterize = {
"min_exposure_time",
"max_exposure_time",
"exposure_time", "exposure_time",
"min_initial_exposure_time",
"max_initial_exposure_time",
"initial_exposure_time", "initial_exposure_time",
"display_width", "display_width",
"display_height", "display_height",

7
src/libslic3r/Semver.cpp Normal file
View file

@ -0,0 +1,7 @@
#include "libslic3r.h"
namespace Slic3r {
Semver SEMVER { SLIC3R_VERSION };
}

View file

@ -61,7 +61,7 @@ extern std::string normalize_utf8_nfc(const char *src);
// Safely rename a file even if the target exists. // Safely rename a file even if the target exists.
// On Windows, the file explorer (or anti-virus or whatever else) often locks the file // On Windows, the file explorer (or anti-virus or whatever else) often locks the file
// for a short while, so the file may not be movable. Retry while we see recoverable errors. // for a short while, so the file may not be movable. Retry while we see recoverable errors.
extern int rename_file(const std::string &from, const std::string &to); extern std::error_code rename_file(const std::string &from, const std::string &to);
// Copy a file, adjust the access attributes, so that the target is writable. // Copy a file, adjust the access attributes, so that the target is writable.
extern int copy_file(const std::string &from, const std::string &to); extern int copy_file(const std::string &from, const std::string &to);

View file

@ -19,6 +19,7 @@
#include <cmath> #include <cmath>
#include "Technologies.hpp" #include "Technologies.hpp"
#include "Semver.hpp"
typedef int32_t coord_t; typedef int32_t coord_t;
typedef double coordf_t; typedef double coordf_t;
@ -92,6 +93,8 @@ inline std::string debug_out_path(const char *name, ...)
namespace Slic3r { namespace Slic3r {
extern Semver SEMVER;
template<typename T, typename Q> template<typename T, typename Q>
inline T unscale(Q v) { return T(v) * T(SCALING_FACTOR); } inline T unscale(Q v) { return T(v) * T(SCALING_FACTOR); }

View file

@ -173,67 +173,247 @@ const std::string& data_dir()
return g_data_dir; return g_data_dir;
} }
#ifdef _WIN32
// The following helpers are borrowed from the LLVM project https://github.com/llvm
namespace WindowsSupport
{
template <typename HandleTraits>
class ScopedHandle {
typedef typename HandleTraits::handle_type handle_type;
handle_type Handle;
ScopedHandle(const ScopedHandle &other) = delete;
void operator=(const ScopedHandle &other) = delete;
public:
ScopedHandle() : Handle(HandleTraits::GetInvalid()) {}
explicit ScopedHandle(handle_type h) : Handle(h) {}
~ScopedHandle() { if (HandleTraits::IsValid(Handle)) HandleTraits::Close(Handle); }
handle_type take() {
handle_type t = Handle;
Handle = HandleTraits::GetInvalid();
return t;
}
ScopedHandle &operator=(handle_type h) {
if (HandleTraits::IsValid(Handle))
HandleTraits::Close(Handle);
Handle = h;
return *this;
}
// True if Handle is valid.
explicit operator bool() const { return HandleTraits::IsValid(Handle) ? true : false; }
operator handle_type() const { return Handle; }
};
struct CommonHandleTraits {
typedef HANDLE handle_type;
static handle_type GetInvalid() { return INVALID_HANDLE_VALUE; }
static void Close(handle_type h) { ::CloseHandle(h); }
static bool IsValid(handle_type h) { return h != GetInvalid(); }
};
typedef ScopedHandle<CommonHandleTraits> ScopedFileHandle;
std::error_code map_windows_error(unsigned windows_error_code)
{
#define MAP_ERR_TO_COND(x, y) case x: return std::make_error_code(std::errc::y)
switch (windows_error_code) {
MAP_ERR_TO_COND(ERROR_ACCESS_DENIED, permission_denied);
MAP_ERR_TO_COND(ERROR_ALREADY_EXISTS, file_exists);
MAP_ERR_TO_COND(ERROR_BAD_UNIT, no_such_device);
MAP_ERR_TO_COND(ERROR_BUFFER_OVERFLOW, filename_too_long);
MAP_ERR_TO_COND(ERROR_BUSY, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_BUSY_DRIVE, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_CANNOT_MAKE, permission_denied);
MAP_ERR_TO_COND(ERROR_CANTOPEN, io_error);
MAP_ERR_TO_COND(ERROR_CANTREAD, io_error);
MAP_ERR_TO_COND(ERROR_CANTWRITE, io_error);
MAP_ERR_TO_COND(ERROR_CURRENT_DIRECTORY, permission_denied);
MAP_ERR_TO_COND(ERROR_DEV_NOT_EXIST, no_such_device);
MAP_ERR_TO_COND(ERROR_DEVICE_IN_USE, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_DIR_NOT_EMPTY, directory_not_empty);
MAP_ERR_TO_COND(ERROR_DIRECTORY, invalid_argument);
MAP_ERR_TO_COND(ERROR_DISK_FULL, no_space_on_device);
MAP_ERR_TO_COND(ERROR_FILE_EXISTS, file_exists);
MAP_ERR_TO_COND(ERROR_FILE_NOT_FOUND, no_such_file_or_directory);
MAP_ERR_TO_COND(ERROR_HANDLE_DISK_FULL, no_space_on_device);
MAP_ERR_TO_COND(ERROR_INVALID_ACCESS, permission_denied);
MAP_ERR_TO_COND(ERROR_INVALID_DRIVE, no_such_device);
MAP_ERR_TO_COND(ERROR_INVALID_FUNCTION, function_not_supported);
MAP_ERR_TO_COND(ERROR_INVALID_HANDLE, invalid_argument);
MAP_ERR_TO_COND(ERROR_INVALID_NAME, invalid_argument);
MAP_ERR_TO_COND(ERROR_LOCK_VIOLATION, no_lock_available);
MAP_ERR_TO_COND(ERROR_LOCKED, no_lock_available);
MAP_ERR_TO_COND(ERROR_NEGATIVE_SEEK, invalid_argument);
MAP_ERR_TO_COND(ERROR_NOACCESS, permission_denied);
MAP_ERR_TO_COND(ERROR_NOT_ENOUGH_MEMORY, not_enough_memory);
MAP_ERR_TO_COND(ERROR_NOT_READY, resource_unavailable_try_again);
MAP_ERR_TO_COND(ERROR_OPEN_FAILED, io_error);
MAP_ERR_TO_COND(ERROR_OPEN_FILES, device_or_resource_busy);
MAP_ERR_TO_COND(ERROR_OUTOFMEMORY, not_enough_memory);
MAP_ERR_TO_COND(ERROR_PATH_NOT_FOUND, no_such_file_or_directory);
MAP_ERR_TO_COND(ERROR_BAD_NETPATH, no_such_file_or_directory);
MAP_ERR_TO_COND(ERROR_READ_FAULT, io_error);
MAP_ERR_TO_COND(ERROR_RETRY, resource_unavailable_try_again);
MAP_ERR_TO_COND(ERROR_SEEK, io_error);
MAP_ERR_TO_COND(ERROR_SHARING_VIOLATION, permission_denied);
MAP_ERR_TO_COND(ERROR_TOO_MANY_OPEN_FILES, too_many_files_open);
MAP_ERR_TO_COND(ERROR_WRITE_FAULT, io_error);
MAP_ERR_TO_COND(ERROR_WRITE_PROTECT, permission_denied);
MAP_ERR_TO_COND(WSAEACCES, permission_denied);
MAP_ERR_TO_COND(WSAEBADF, bad_file_descriptor);
MAP_ERR_TO_COND(WSAEFAULT, bad_address);
MAP_ERR_TO_COND(WSAEINTR, interrupted);
MAP_ERR_TO_COND(WSAEINVAL, invalid_argument);
MAP_ERR_TO_COND(WSAEMFILE, too_many_files_open);
MAP_ERR_TO_COND(WSAENAMETOOLONG, filename_too_long);
default:
return std::error_code(windows_error_code, std::system_category());
}
#undef MAP_ERR_TO_COND
}
static std::error_code rename_internal(HANDLE from_handle, const std::wstring &wide_to, bool replace_if_exists)
{
std::vector<char> rename_info_buf(sizeof(FILE_RENAME_INFO) - sizeof(wchar_t) + (wide_to.size() * sizeof(wchar_t)));
FILE_RENAME_INFO &rename_info = *reinterpret_cast<FILE_RENAME_INFO*>(rename_info_buf.data());
rename_info.ReplaceIfExists = replace_if_exists;
rename_info.RootDirectory = 0;
rename_info.FileNameLength = DWORD(wide_to.size() * sizeof(wchar_t));
std::copy(wide_to.begin(), wide_to.end(), &rename_info.FileName[0]);
::SetLastError(ERROR_SUCCESS);
if (! ::SetFileInformationByHandle(from_handle, FileRenameInfo, &rename_info, (DWORD)rename_info_buf.size())) {
unsigned Error = GetLastError();
if (Error == ERROR_SUCCESS)
Error = ERROR_CALL_NOT_IMPLEMENTED; // Wine doesn't always set error code.
return map_windows_error(Error);
}
return std::error_code();
}
static std::error_code real_path_from_handle(HANDLE H, std::wstring &buffer)
{
buffer.resize(MAX_PATH + 1);
DWORD CountChars = ::GetFinalPathNameByHandleW(H, (LPWSTR)buffer.data(), (DWORD)buffer.size() - 1, FILE_NAME_NORMALIZED);
if (CountChars > buffer.size()) {
// The buffer wasn't big enough, try again. In this case the return value *does* indicate the size of the null terminator.
buffer.resize((size_t)CountChars);
CountChars = ::GetFinalPathNameByHandleW(H, (LPWSTR)buffer.data(), (DWORD)buffer.size() - 1, FILE_NAME_NORMALIZED);
}
if (CountChars == 0)
return map_windows_error(GetLastError());
buffer.resize(CountChars);
return std::error_code();
}
std::error_code rename(const std::string &from, const std::string &to)
{
// Convert to utf-16.
std::wstring wide_from = boost::nowide::widen(from);
std::wstring wide_to = boost::nowide::widen(to);
ScopedFileHandle from_handle;
// Retry this a few times to defeat badly behaved file system scanners.
for (unsigned retry = 0; retry != 200; ++ retry) {
if (retry != 0)
::Sleep(10);
from_handle = ::CreateFileW((LPWSTR)wide_from.data(), GENERIC_READ | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (from_handle)
break;
}
if (! from_handle)
return map_windows_error(GetLastError());
// We normally expect this loop to succeed after a few iterations. If it
// requires more than 200 tries, it's more likely that the failures are due to
// a true error, so stop trying.
for (unsigned retry = 0; retry != 200; ++ retry) {
auto errcode = rename_internal(from_handle, wide_to, true);
if (errcode == std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category())) {
// Wine doesn't support SetFileInformationByHandle in rename_internal.
// Fall back to MoveFileEx.
if (std::error_code errcode2 = real_path_from_handle(from_handle, wide_from))
return errcode2;
if (::MoveFileExW((LPCWSTR)wide_from.data(), (LPCWSTR)wide_to.data(), MOVEFILE_REPLACE_EXISTING))
return std::error_code();
return map_windows_error(GetLastError());
}
if (! errcode || errcode != std::errc::permission_denied)
return errcode;
// The destination file probably exists and is currently open in another
// process, either because the file was opened without FILE_SHARE_DELETE or
// it is mapped into memory (e.g. using MemoryBuffer). Rename it in order to
// move it out of the way of the source file. Use FILE_FLAG_DELETE_ON_CLOSE
// to arrange for the destination file to be deleted when the other process
// closes it.
ScopedFileHandle to_handle(::CreateFileW((LPCWSTR)wide_to.data(), GENERIC_READ | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL));
if (! to_handle) {
auto errcode = map_windows_error(GetLastError());
// Another process might have raced with us and moved the existing file
// out of the way before we had a chance to open it. If that happens, try
// to rename the source file again.
if (errcode == std::errc::no_such_file_or_directory)
continue;
return errcode;
}
BY_HANDLE_FILE_INFORMATION FI;
if (! ::GetFileInformationByHandle(to_handle, &FI))
return map_windows_error(GetLastError());
// Try to find a unique new name for the destination file.
for (unsigned unique_id = 0; unique_id != 200; ++ unique_id) {
std::wstring tmp_filename = wide_to + L".tmp" + std::to_wstring(unique_id);
std::error_code errcode = rename_internal(to_handle, tmp_filename, false);
if (errcode) {
if (errcode == std::make_error_code(std::errc::file_exists) || errcode == std::make_error_code(std::errc::permission_denied)) {
// Again, another process might have raced with us and moved the file
// before we could move it. Check whether this is the case, as it
// might have caused the permission denied error. If that was the
// case, we don't need to move it ourselves.
ScopedFileHandle to_handle2(::CreateFileW((LPCWSTR)wide_to.data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
if (! to_handle2) {
auto errcode = map_windows_error(GetLastError());
if (errcode == std::errc::no_such_file_or_directory)
break;
return errcode;
}
BY_HANDLE_FILE_INFORMATION FI2;
if (! ::GetFileInformationByHandle(to_handle2, &FI2))
return map_windows_error(GetLastError());
if (FI.nFileIndexHigh != FI2.nFileIndexHigh || FI.nFileIndexLow != FI2.nFileIndexLow || FI.dwVolumeSerialNumber != FI2.dwVolumeSerialNumber)
break;
continue;
}
return errcode;
}
break;
}
// Okay, the old destination file has probably been moved out of the way at
// this point, so try to rename the source file again. Still, another
// process might have raced with us to create and open the destination
// file, so we need to keep doing this until we succeed.
}
// The most likely root cause.
return std::make_error_code(std::errc::permission_denied);
}
} // namespace WindowsSupport
#endif /* _WIN32 */
// borrowed from LVVM lib/Support/Windows/Path.inc // borrowed from LVVM lib/Support/Windows/Path.inc
int rename_file(const std::string &from, const std::string &to) std::error_code rename_file(const std::string &from, const std::string &to)
{ {
int ec = 0;
#ifdef _WIN32 #ifdef _WIN32
return WindowsSupport::rename(from, to);
// Convert to utf-16.
std::wstring wide_from = boost::nowide::widen(from);
std::wstring wide_to = boost::nowide::widen(to);
// Retry while we see recoverable errors.
// System scanners (eg. indexer) might open the source file when it is written
// and closed.
bool TryReplace = true;
// This loop may take more than 2000 x 1ms to finish.
for (int i = 0; i < 2000; ++ i) {
if (i > 0)
// Sleep 1ms
::Sleep(1);
if (TryReplace) {
// Try ReplaceFile first, as it is able to associate a new data stream
// with the destination even if the destination file is currently open.
if (::ReplaceFileW(wide_to.data(), wide_from.data(), NULL, 0, NULL, NULL))
return 0;
DWORD ReplaceError = ::GetLastError();
ec = -1; // ReplaceError
// If ReplaceFileW returned ERROR_UNABLE_TO_MOVE_REPLACEMENT or
// ERROR_UNABLE_TO_MOVE_REPLACEMENT_2, retry but only use MoveFileExW().
if (ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT ||
ReplaceError == ERROR_UNABLE_TO_MOVE_REPLACEMENT_2) {
TryReplace = false;
continue;
}
// If ReplaceFileW returned ERROR_UNABLE_TO_REMOVE_REPLACED, retry
// using ReplaceFileW().
if (ReplaceError == ERROR_UNABLE_TO_REMOVE_REPLACED)
continue;
// We get ERROR_FILE_NOT_FOUND if the destination file is missing.
// MoveFileEx can handle this case.
if (ReplaceError != ERROR_ACCESS_DENIED && ReplaceError != ERROR_FILE_NOT_FOUND && ReplaceError != ERROR_SHARING_VIOLATION)
break;
}
if (::MoveFileExW(wide_from.c_str(), wide_to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING))
return 0;
DWORD MoveError = ::GetLastError();
ec = -1; // MoveError
if (MoveError != ERROR_ACCESS_DENIED && MoveError != ERROR_SHARING_VIOLATION)
break;
}
#else #else
boost::nowide::remove(to.c_str()); boost::nowide::remove(to.c_str());
ec = boost::nowide::rename(from.c_str(), to.c_str()); return std::make_error_code(static_cast<std::errc>(boost::nowide::rename(from.c_str(), to.c_str())));
#endif #endif
return ec;
} }
int copy_file(const std::string &from, const std::string &to) int copy_file(const std::string &from, const std::string &to)

View file

@ -5,3 +5,5 @@ add_library(semver STATIC
semver.c semver.c
semver.h semver.h
) )
encoding_check(semver)

View file

@ -163,6 +163,8 @@ endif ()
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
encoding_check(libslic3r_gui)
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES}) target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES})
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)

View file

@ -366,7 +366,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
// Snapshot header. // Snapshot header.
snapshot.time_captured = Slic3r::Utils::get_current_time_utc(); snapshot.time_captured = Slic3r::Utils::get_current_time_utc();
snapshot.id = Slic3r::Utils::format_time_ISO8601Z(snapshot.time_captured); snapshot.id = Slic3r::Utils::format_time_ISO8601Z(snapshot.time_captured);
snapshot.slic3r_version_captured = *Semver::parse(SLIC3R_VERSION); // XXX: have Semver Slic3r version snapshot.slic3r_version_captured = Slic3r::SEMVER;
snapshot.comment = comment; snapshot.comment = comment;
snapshot.reason = reason; snapshot.reason = reason;
// Active presets at the time of the snapshot. // Active presets at the time of the snapshot.

View file

@ -8,8 +8,8 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include "libslic3r/Semver.hpp"
#include "Version.hpp" #include "Version.hpp"
#include "../Utils/Semver.hpp"
namespace Slic3r { namespace Slic3r {

View file

@ -15,7 +15,6 @@ namespace Slic3r {
namespace GUI { namespace GUI {
namespace Config { namespace Config {
static const Semver s_current_slic3r_semver(SLIC3R_VERSION);
// Optimized lexicographic compare of two pre-release versions, ignoring the numeric suffix. // Optimized lexicographic compare of two pre-release versions, ignoring the numeric suffix.
static int compare_prerelease(const char *p1, const char *p2) static int compare_prerelease(const char *p1, const char *p2)
@ -64,7 +63,7 @@ bool Version::is_slic3r_supported(const Semver &slic3r_version) const
bool Version::is_current_slic3r_supported() const bool Version::is_current_slic3r_supported() const
{ {
return this->is_slic3r_supported(s_current_slic3r_semver); return this->is_slic3r_supported(Slic3r::SEMVER);
} }
#if 0 #if 0

View file

@ -7,7 +7,7 @@
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include "libslic3r/FileParserError.hpp" #include "libslic3r/FileParserError.hpp"
#include "../Utils/Semver.hpp" #include "libslic3r/Semver.hpp"
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {

View file

@ -609,10 +609,12 @@ void Bed3D::render_default(bool bottom) const
if (!has_model && !bottom) if (!has_model && !bottom)
{ {
// draw background // draw background
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f)); glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f));
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f)); glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data())); glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()));
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount)); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
glsafe(::glDepthMask(GL_TRUE));
} }
// draw grid // draw grid

View file

@ -76,9 +76,9 @@ void CopyrightsDialog::fill_entries()
{ {
m_entries = { m_entries = {
{ "wxWidgets" , "2019 wxWidgets" , "https://www.wxwidgets.org/" }, { "wxWidgets" , "2019 wxWidgets" , "https://www.wxwidgets.org/" },
{ "OpenGL" , "1997-2019 The Khronos™ Group Inc" , "https://www.opengl.org/" }, { "OpenGL" , "1997-2019 The Khronos™ Group Inc" , "https://www.opengl.org/" },
{ "GNU gettext" , "1998, 2019 Free Software Foundation, Inc." , "https://www.gnu.org/software/gettext/" }, { "GNU gettext" , "1998, 2019 Free Software Foundation, Inc." , "https://www.gnu.org/software/gettext/" },
{ "PoEdit" , "2019 Václav Slavík" , "https://poedit.net/" }, { "PoEdit" , "2019 Václav Slavík" , "https://poedit.net/" },
{ "ImGUI" , "2014-2019 Omar Cornut" , "https://github.com/ocornut/imgui" }, { "ImGUI" , "2014-2019 Omar Cornut" , "https://github.com/ocornut/imgui" },
{ "Eigen" , "" , "http://eigen.tuxfamily.org" }, { "Eigen" , "" , "http://eigen.tuxfamily.org" },
{ "ADMesh" , "1995, 1996 Anthony D. Martin; " { "ADMesh" , "1995, 1996 Anthony D. Martin; "
@ -110,7 +110,9 @@ void CopyrightsDialog::fill_entries()
, "Based on original by fabian \"ryg\" giesen v1.04. " , "Based on original by fabian \"ryg\" giesen v1.04. "
"Custom version, modified by Yann Collet" , "https://github.com/Cyan4973/RygsDXTc" }, "Custom version, modified by Yann Collet" , "https://github.com/Cyan4973/RygsDXTc" },
{ "Icons for STL and GCODE files." { "Icons for STL and GCODE files."
, "Akira Yasuda" , "http://3dp0.com/icons-for-stl-and-gcode/" } , "Akira Yasuda" , "http://3dp0.com/icons-for-stl-and-gcode/" },
{ "AppImage packaging for Linux using AppImageKit"
, "2004-2019 Simon Peter and contributors" , "https://appimage.org/" }
}; };
} }

View file

@ -15,9 +15,13 @@
#include <boost/nowide/fstream.hpp> #include <boost/nowide/fstream.hpp>
#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/property_tree/exceptions.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <wx/string.h>
#include "I18N.hpp"
namespace Slic3r { namespace Slic3r {
static const std::string VENDOR_PREFIX = "vendor:"; static const std::string VENDOR_PREFIX = "vendor:";
@ -58,7 +62,7 @@ void AppConfig::set_defaults()
if (!get("use_legacy_opengl").empty()) if (!get("use_legacy_opengl").empty())
erase("", "use_legacy_opengl"); erase("", "use_legacy_opengl");
#if __APPLE__ #ifdef __APPLE__
if (get("use_retina_opengl").empty()) if (get("use_retina_opengl").empty())
set("use_retina_opengl", "1"); set("use_retina_opengl", "1");
#endif #endif
@ -90,7 +94,15 @@ void AppConfig::load()
namespace pt = boost::property_tree; namespace pt = boost::property_tree;
pt::ptree tree; pt::ptree tree;
boost::nowide::ifstream ifs(AppConfig::config_path()); boost::nowide::ifstream ifs(AppConfig::config_path());
pt::read_ini(ifs, tree); try {
pt::read_ini(ifs, tree);
} catch (pt::ptree_error& ex) {
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
throw std::runtime_error(
_utf8(L("Error parsing PrusaSlicer config file, it is probably corrupted. "
"Try to manualy delete the file to recover from the error. Your user profiles will not be affected.")) +
"\n\n" + AppConfig::config_path() + "\n\n" + ex.what());
}
// 2) Parse the property_tree, extract the sections and key / value pairs. // 2) Parse the property_tree, extract the sections and key / value pairs.
for (const auto &section : tree) { for (const auto &section : tree) {

View file

@ -6,7 +6,7 @@
#include <string> #include <string>
#include "libslic3r/Config.hpp" #include "libslic3r/Config.hpp"
#include "slic3r/Utils/Semver.hpp" #include "libslic3r/Semver.hpp"
namespace Slic3r { namespace Slic3r {

View file

@ -151,7 +151,12 @@ void BackgroundSlicingProcess::thread_proc()
} catch (CanceledException & /* ex */) { } catch (CanceledException & /* ex */) {
// Canceled, this is all right. // Canceled, this is all right.
assert(m_print->canceled()); assert(m_print->canceled());
} catch (std::exception &ex) { } catch (const std::bad_alloc& ex) {
wxString errmsg = wxString::Format(_(L("%s has encountered an error. It was likely caused by running out of memory. "
"If you are sure you have enough RAM on your system, this may also be a bug and we would "
"be glad if you reported it.")), SLIC3R_APP_NAME);
error = errmsg.ToStdString() + "\n\n" + std::string(ex.what());
} catch (std::exception &ex) {
error = ex.what(); error = ex.what();
} catch (...) { } catch (...) {
error = "Unknown C++ exception."; error = "Unknown C++ exception.";

View file

@ -25,6 +25,7 @@
#include "PresetBundle.hpp" #include "PresetBundle.hpp"
#include "GUI.hpp" #include "GUI.hpp"
#include "GUI_Utils.hpp" #include "GUI_Utils.hpp"
#include "slic3r/Config/Snapshot.hpp"
#include "slic3r/Utils/PresetUpdater.hpp" #include "slic3r/Utils/PresetUpdater.hpp"
@ -32,6 +33,10 @@ namespace Slic3r {
namespace GUI { namespace GUI {
using Config::Snapshot;
using Config::SnapshotDB;
// Printer model picker GUI control // Printer model picker GUI control
struct PrinterPickerEvent : public wxEvent struct PrinterPickerEvent : public wxEvent
@ -1025,15 +1030,33 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
// Decide whether to create snapshot based on run_reason and the reset profile checkbox // Decide whether to create snapshot based on run_reason and the reset profile checkbox
bool snapshot = true; bool snapshot = true;
Snapshot::Reason snapshot_reason = Snapshot::SNAPSHOT_UPGRADE;
switch (run_reason) { switch (run_reason) {
case ConfigWizard::RR_DATA_EMPTY: snapshot = false; break; case ConfigWizard::RR_DATA_EMPTY:
case ConfigWizard::RR_DATA_LEGACY: snapshot = true; break; snapshot = false;
case ConfigWizard::RR_DATA_INCOMPAT: snapshot = false; break; // In this case snapshot is done by PresetUpdater with the appropriate reason break;
case ConfigWizard::RR_USER: snapshot = page_welcome->reset_user_profile(); break; case ConfigWizard::RR_DATA_LEGACY:
snapshot = true;
break;
case ConfigWizard::RR_DATA_INCOMPAT:
// In this case snapshot has already been taken by
// PresetUpdater with the appropriate reason
snapshot = false;
break;
case ConfigWizard::RR_USER:
snapshot = page_welcome->reset_user_profile();
snapshot_reason = Snapshot::SNAPSHOT_USER;
break;
} }
if (snapshot) {
SnapshotDB::singleton().take_snapshot(*app_config, snapshot_reason);
}
if (install_bundles.size() > 0) { if (install_bundles.size() > 0) {
// Install bundles from resources. // Install bundles from resources.
updater->install_bundles_rsrc(std::move(install_bundles), snapshot); // Don't create snapshot - we've already done that above if applicable.
updater->install_bundles_rsrc(std::move(install_bundles), false);
} else { } else {
BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources"; BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources";
} }

View file

@ -206,8 +206,8 @@ void Field::get_value_by_opt_type(wxString& str)
const wxString msg_text = wxString::Format(_(L("Do you mean %s%% instead of %s %s?\n" const wxString msg_text = wxString::Format(_(L("Do you mean %s%% instead of %s %s?\n"
"Select YES if you want to change this value to %s%%, \n" "Select YES if you want to change this value to %s%%, \n"
"or NO if you are sure that %s %s is a correct value.")), stVal, stVal, sidetext, stVal, stVal, sidetext); "or NO if you are sure that %s %s is a correct value.")), stVal, stVal, sidetext, stVal, stVal, sidetext);
auto dialog = new wxMessageDialog(m_parent, msg_text, _(L("Parameter validation")), wxICON_WARNING | wxYES | wxNO); wxMessageDialog dialog(m_parent, msg_text, _(L("Parameter validation")), wxICON_WARNING | wxYES | wxNO);
if (dialog->ShowModal() == wxID_YES) { if (dialog.ShowModal() == wxID_YES) {
set_value(wxString::Format("%s%%", stVal), false/*true*/); set_value(wxString::Format("%s%%", stVal), false/*true*/);
str += "%%"; str += "%%";
} }
@ -559,7 +559,16 @@ void SpinCtrl::BUILD() {
break; break;
} }
const int min_val = m_opt.min == INT_MIN ? 0: m_opt.min; const int min_val = m_opt.min == INT_MIN
#ifdef __WXOSX__
// We will forcibly set the input value for SpinControl, since the value
// inserted from the keyboard is not updated under OSX.
// So, we can't set min control value bigger then 0.
// Otherwise, it couldn't be possible to input from keyboard value
// less then min_val.
|| m_opt.min > 0
#endif
? 0 : m_opt.min;
const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647; const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647;
auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size,
@ -628,16 +637,24 @@ void SpinCtrl::BUILD() {
void SpinCtrl::propagate_value() void SpinCtrl::propagate_value()
{ {
if (suppress_propagating) if (suppress_propagation)
return; return;
suppress_propagating = true; suppress_propagation = true;
if (tmp_value == UNDEF_VALUE) { if (tmp_value == UNDEF_VALUE) {
on_kill_focus(); on_kill_focus();
} else { } else {
#ifdef __WXOSX__
// check input value for minimum
if (m_opt.min > 0 && tmp_value < m_opt.min) {
wxSpinCtrl* spin = static_cast<wxSpinCtrl*>(window);
spin->SetValue(m_opt.min);
spin->GetText()->SetInsertionPointEnd();
}
#endif
on_change_field(); on_change_field();
} }
suppress_propagating = false; suppress_propagation = false;
} }
void SpinCtrl::msw_rescale() void SpinCtrl::msw_rescale()
@ -1021,11 +1038,12 @@ void ColourPicker::BUILD()
// Validate the color // Validate the color
wxString clr_str(m_opt.get_default_value<ConfigOptionStrings>()->get_at(m_opt_idx)); wxString clr_str(m_opt.get_default_value<ConfigOptionStrings>()->get_at(m_opt_idx));
wxColour clr(clr_str); wxColour clr(clr_str);
if (! clr.IsOk()) { if (clr_str.IsEmpty() || !clr.IsOk()) {
clr = wxTransparentColour; clr = wxTransparentColour;
} }
auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size); auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT); temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
// // recast as a wxWindow to fit the calling convention // // recast as a wxWindow to fit the calling convention
@ -1036,17 +1054,59 @@ void ColourPicker::BUILD()
temp->SetToolTip(get_tooltip_text(clr_str)); temp->SetToolTip(get_tooltip_text(clr_str));
} }
void ColourPicker::set_undef_value(wxColourPickerCtrl* field)
{
field->SetColour(wxTransparentColour);
wxButton* btn = dynamic_cast<wxButton*>(field->GetPickerCtrl());
wxBitmap bmp = btn->GetBitmap();
wxMemoryDC dc(bmp);
dc.SetTextForeground(*wxWHITE);
dc.SetFont(wxGetApp().normal_font());
const wxRect rect = wxRect(0, 0, bmp.GetWidth(), bmp.GetHeight());
dc.DrawLabel("undef", rect, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL);
dc.SelectObject(wxNullBitmap);
btn->SetBitmapLabel(bmp);
}
void ColourPicker::set_value(const boost::any& value, bool change_event)
{
m_disable_change_event = !change_event;
const wxString clr_str(boost::any_cast<wxString>(value));
auto field = dynamic_cast<wxColourPickerCtrl*>(window);
wxColour clr(clr_str);
if (clr_str.IsEmpty() || !clr.IsOk())
set_undef_value(field);
else
field->SetColour(clr);
m_disable_change_event = false;
}
boost::any& ColourPicker::get_value() boost::any& ColourPicker::get_value()
{ {
// boost::any m_value;
auto colour = static_cast<wxColourPickerCtrl*>(window)->GetColour(); auto colour = static_cast<wxColourPickerCtrl*>(window)->GetColour();
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue()); if (colour == wxTransparentColour)
m_value = clr_str.ToStdString(); m_value = std::string("");
else {
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue());
m_value = clr_str.ToStdString();
}
return m_value; return m_value;
} }
void ColourPicker::msw_rescale()
{
Field::msw_rescale();
wxColourPickerCtrl* field = dynamic_cast<wxColourPickerCtrl*>(window);
if (field->GetColour() == wxTransparentColour)
set_undef_value(field);
}
void PointCtrl::BUILD() void PointCtrl::BUILD()
{ {
auto temp = new wxBoxSizer(wxHORIZONTAL); auto temp = new wxBoxSizer(wxHORIZONTAL);

View file

@ -406,6 +406,8 @@ public:
class ColourPicker : public Field { class ColourPicker : public Field {
using Field::Field; using Field::Field;
void set_undef_value(wxColourPickerCtrl* field);
public: public:
ColourPicker(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} ColourPicker(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
ColourPicker(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} ColourPicker(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
@ -419,13 +421,9 @@ public:
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value); dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value);
m_disable_change_event = false; m_disable_change_event = false;
} }
void set_value(const boost::any& value, bool change_event = false) { void set_value(const boost::any& value, bool change_event = false) override;
m_disable_change_event = !change_event;
dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(boost::any_cast<wxString>(value));
m_disable_change_event = false;
}
boost::any& get_value() override; boost::any& get_value() override;
void msw_rescale() override;
void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); }; void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); };
void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); }; void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); };

View file

@ -2474,6 +2474,13 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
evt.SetY(evt.GetY() * scale); evt.SetY(evt.GetY() * scale);
#endif #endif
#ifdef __WXMSW__
// For some reason the Idle event is not being generated after the mouse scroll event in case of scrolling with the two fingers on the touch pad,
// if the event is not allowed to be passed further.
// https://github.com/prusa3d/PrusaSlicer/issues/2750
evt.Skip();
#endif /* __WXMSW__ */
// Performs layers editing updates, if enabled // Performs layers editing updates, if enabled
if (is_layers_editing_enabled()) if (is_layers_editing_enabled())
{ {

View file

@ -126,7 +126,16 @@ static void generic_exception_handle()
try { try {
throw; throw;
} catch (const std::exception &ex) { } catch (const std::bad_alloc& ex) {
// bad_alloc in main thread is most likely fatal. Report immediately to the user (wxLogError would be delayed)
// and terminate the app so it is at least certain to happen now.
wxString errmsg = wxString::Format(_(L("%s has encountered an error. It was likely caused by running out of memory. "
"If you are sure you have enough RAM on your system, this may also be a bug and we would "
"be glad if you reported it.\n\nThe application will now terminate.")), SLIC3R_APP_NAME);
wxMessageBox(errmsg + "\n\n" + wxString(ex.what()), _(L("Fatal error")), wxOK | wxICON_ERROR);
BOOST_LOG_TRIVIAL(error) << boost::format("std::bad_alloc exception: %1%") % ex.what();
std::terminate();
} catch (const std::exception& ex) {
wxLogError("Internal error: %s", ex.what()); wxLogError("Internal error: %s", ex.what());
BOOST_LOG_TRIVIAL(error) << boost::format("Uncaught exception: %1%") % ex.what(); BOOST_LOG_TRIVIAL(error) << boost::format("Uncaught exception: %1%") % ex.what();
throw; throw;
@ -284,6 +293,20 @@ bool GUI_App::on_init_inner()
config_wizard_startup(app_conf_exists); config_wizard_startup(app_conf_exists);
preset_updater->slic3r_update_notify(); preset_updater->slic3r_update_notify();
preset_updater->sync(preset_bundle); preset_updater->sync(preset_bundle);
const GLCanvas3DManager::GLInfo &glinfo = GLCanvas3DManager::get_gl_info();
if (! glinfo.is_version_greater_or_equal_to(2, 0)) {
// Complain about the OpenGL version.
wxString message = wxString::Format(
_(L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n"
"while OpenGL version %s, render %s, vendor %s was detected.")), wxString(glinfo.get_version()), wxString(glinfo.get_renderer()), wxString(glinfo.get_vendor()));
message += "\n";
message += _(L("You may need to update your graphics card driver."));
#ifdef _WIN32
message += "\n";
message += _(L("As a workaround, you may run PrusaSlicer with a software rendered 3D graphics by running prusa-slicer.exe with the --sw_renderer parameter."));
#endif
wxMessageBox(message, wxString("PrusaSlicer - ") + _(L("Unsupported OpenGL version")), wxOK | wxICON_ERROR);
}
}); });
} }
}); });

View file

@ -134,7 +134,11 @@ ObjectList::ObjectList(wxWindow* parent) :
selection_changed(); selection_changed();
#ifndef __WXMSW__ #ifndef __WXMSW__
set_tooltip_for_item(get_mouse_position_in_control()); set_tooltip_for_item(get_mouse_position_in_control());
#endif //__WXMSW__ #endif //__WXMSW__
#ifndef __WXOSX__
list_manipulation();
#endif //__WXOSX__
}); });
#ifdef __WXOSX__ #ifdef __WXOSX__
@ -169,7 +173,7 @@ ObjectList::ObjectList(wxWindow* parent) :
#ifdef __WXMSW__ #ifdef __WXMSW__
GetMainWindow()->Bind(wxEVT_MOTION, [this](wxMouseEvent& event) { GetMainWindow()->Bind(wxEVT_MOTION, [this](wxMouseEvent& event) {
set_tooltip_for_item(/*event.GetPosition()*/get_mouse_position_in_control()); set_tooltip_for_item(get_mouse_position_in_control());
event.Skip(); event.Skip();
}); });
#endif //__WXMSW__ #endif //__WXMSW__
@ -330,28 +334,34 @@ void ObjectList::set_tooltip_for_item(const wxPoint& pt)
* Just this->SetToolTip(tooltip) => has no effect. * Just this->SetToolTip(tooltip) => has no effect.
*/ */
if (!item) if (!item || GetSelectedItemsCount() > 1)
{ {
GetMainWindow()->SetToolTip(""); // hide tooltip GetMainWindow()->SetToolTip(""); // hide tooltip
return; return;
} }
if (col->GetTitle() == _(L("Editing")) && GetSelectedItemsCount()<2) wxString tooltip = "";
GetMainWindow()->SetToolTip(_(L("Right button click the icon to change the object settings")));
else if (col->GetTitle() == _("Name")) if (col->GetTitle() == _(L("Editing")))
{ #ifdef __WXOSX__
#ifdef __WXMSW__ tooltip = _(L("Right button click the icon to change the object settings"));
if (pt.x < 2 * wxGetApp().em_unit() || pt.x > 4 * wxGetApp().em_unit()) { #else
GetMainWindow()->SetToolTip(""); // hide tooltip tooltip = _(L("Click the icon to change the object settings"));
return;
}
#endif //__WXMSW__ #endif //__WXMSW__
else if (col->GetTitle() == " ")
#ifdef __WXOSX__
tooltip = _(L("Right button click the icon to change the object printable property"));
#else
tooltip = _(L("Click the icon to change the object printable property"));
#endif //__WXMSW__
else if (col->GetTitle() == _("Name") && (pt.x >= 2 * wxGetApp().em_unit() && pt.x <= 4 * wxGetApp().em_unit()))
{
int obj_idx, vol_idx; int obj_idx, vol_idx;
get_selected_item_indexes(obj_idx, vol_idx, item); get_selected_item_indexes(obj_idx, vol_idx, item);
GetMainWindow()->SetToolTip(get_mesh_errors_list(obj_idx, vol_idx)); tooltip = get_mesh_errors_list(obj_idx, vol_idx);
} }
else
GetMainWindow()->SetToolTip(""); // hide tooltip GetMainWindow()->SetToolTip(tooltip);
} }
wxPoint ObjectList::get_mouse_position_in_control() wxPoint ObjectList::get_mouse_position_in_control()
@ -744,6 +754,11 @@ void ObjectList::OnChar(wxKeyEvent& event)
#endif /* __WXOSX__ */ #endif /* __WXOSX__ */
void ObjectList::OnContextMenu(wxDataViewEvent&) void ObjectList::OnContextMenu(wxDataViewEvent&)
{
list_manipulation();
}
void ObjectList::list_manipulation()
{ {
wxDataViewItem item; wxDataViewItem item;
wxDataViewColumn* col; wxDataViewColumn* col;
@ -2291,14 +2306,14 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
{ {
std::vector<bool> print_idicator(model_object->instances.size()); std::vector<bool> print_idicator(model_object->instances.size());
for (int i = 0; i < model_object->instances.size(); ++i) for (int i = 0; i < model_object->instances.size(); ++i)
print_idicator[i] = model_object->instances[i]->is_printable(); print_idicator[i] = model_object->instances[i]->printable;
const wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx); const wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx);
m_objects_model->AddInstanceChild(object_item, print_idicator); m_objects_model->AddInstanceChild(object_item, print_idicator);
Expand(m_objects_model->GetInstanceRootItem(object_item)); Expand(m_objects_model->GetInstanceRootItem(object_item));
} }
else else
m_objects_model->SetPrintableState(model_object->instances[0]->is_printable() ? piPrintable : piUnprintable, obj_idx); m_objects_model->SetPrintableState(model_object->instances[0]->printable ? piPrintable : piUnprintable, obj_idx);
// add settings to the object, if it has those // add settings to the object, if it has those
add_settings_item(item, &model_object->config); add_settings_item(item, &model_object->config);

View file

@ -358,6 +358,7 @@ private:
// void OnChar(wxKeyEvent& event); // void OnChar(wxKeyEvent& event);
#endif /* __WXOSX__ */ #endif /* __WXOSX__ */
void OnContextMenu(wxDataViewEvent &event); void OnContextMenu(wxDataViewEvent &event);
void list_manipulation();
void OnBeginDrag(wxDataViewEvent &event); void OnBeginDrag(wxDataViewEvent &event);
void OnDropPossible(wxDataViewEvent &event); void OnDropPossible(wxDataViewEvent &event);

View file

@ -153,8 +153,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
auto manifold_warning_icon = [this](wxWindow* parent) { auto manifold_warning_icon = [this](wxWindow* parent) {
m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap); m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap);
auto sizer = new wxBoxSizer(wxHORIZONTAL); // auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_fix_throught_netfab_bitmap); // sizer->Add(m_fix_throught_netfab_bitmap);
if (is_windows10()) if (is_windows10())
m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent &e) m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent &e)
@ -167,17 +167,19 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list()); update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list());
}); });
return sizer; // return sizer;
return m_fix_throught_netfab_bitmap;
}; };
line.append_widget(manifold_warning_icon); // line.append_widget(manifold_warning_icon);
line.near_label_widget = manifold_warning_icon;
def.label = ""; def.label = "";
def.gui_type = "legend"; def.gui_type = "legend";
def.tooltip = L("Object name"); def.tooltip = L("Object name");
#ifdef __APPLE__ #ifdef __APPLE__
def.width = 19; def.width = 20;
#else #else
def.width = 21; def.width = 22;
#endif #endif
def.set_default_value(new ConfigOptionString{ " " }); def.set_default_value(new ConfigOptionString{ " " });
line.append_option(Option(def, "object_name")); line.append_option(Option(def, "object_name"));
@ -392,10 +394,19 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
// call back for a rescale of button "Set uniform scale" // call back for a rescale of button "Set uniform scale"
m_og->rescale_near_label_widget = [this](wxWindow* win) { m_og->rescale_near_label_widget = [this](wxWindow* win) {
// rescale lock icon
auto *ctrl = dynamic_cast<LockButton*>(win); auto *ctrl = dynamic_cast<LockButton*>(win);
if (ctrl == nullptr) if (ctrl != nullptr) {
ctrl->msw_rescale();
return; return;
ctrl->msw_rescale(); }
if (win == m_fix_throught_netfab_bitmap)
return;
// rescale "place" of the empty icon (to correct layout of the "Size" and "Scale")
if (dynamic_cast<wxStaticBitmap*>(win) != nullptr)
win->SetMinSize(create_scaled_bitmap(m_parent, "one_layer_lock_on.png").GetSize());
}; };
} }
@ -685,6 +696,7 @@ void ObjectManipulation::emulate_kill_focus()
void ObjectManipulation::update_warning_icon_state(const wxString& tooltip) void ObjectManipulation::update_warning_icon_state(const wxString& tooltip)
{ {
m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp()); m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp());
m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0,0) : m_manifold_warning_bmp.bmp().GetSize());
m_fix_throught_netfab_bitmap->SetToolTip(tooltip); m_fix_throught_netfab_bitmap->SetToolTip(tooltip);
} }
@ -919,7 +931,10 @@ void ObjectManipulation::msw_rescale()
{ {
msw_rescale_word_local_combo(m_word_local_combo); msw_rescale_word_local_combo(m_word_local_combo);
m_manifold_warning_bmp.msw_rescale(); m_manifold_warning_bmp.msw_rescale();
m_fix_throught_netfab_bitmap->SetBitmap(m_manifold_warning_bmp.bmp());
const wxString& tooltip = m_fix_throught_netfab_bitmap->GetToolTipText();
m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp());
m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0, 0) : m_manifold_warning_bmp.bmp().GetSize());
m_mirror_bitmap_on.msw_rescale(); m_mirror_bitmap_on.msw_rescale();
m_mirror_bitmap_off.msw_rescale(); m_mirror_bitmap_off.msw_rescale();

View file

@ -62,7 +62,7 @@ template<class F> typename F::FN winapi_get_function(const wchar_t *dll, const c
static HINSTANCE dll_handle = LoadLibraryExW(dll, nullptr, 0); static HINSTANCE dll_handle = LoadLibraryExW(dll, nullptr, 0);
if (dll_handle == nullptr) { return nullptr; } if (dll_handle == nullptr) { return nullptr; }
return (F::FN)GetProcAddress(dll_handle, fn_name); return (typename F::FN)GetProcAddress(dll_handle, fn_name);
} }
#endif #endif

View file

@ -64,6 +64,12 @@ public:
m_prev_scale_factor = m_scale_factor; m_prev_scale_factor = m_scale_factor;
m_normal_font = get_default_font_for_dpi(dpi); m_normal_font = get_default_font_for_dpi(dpi);
/* Because of default window font is a primary display font,
* We should set correct font for window before getting em_unit value.
*/
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
this->SetFont(m_normal_font);
#endif
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window. // initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1); m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
@ -72,6 +78,8 @@ public:
this->Bind(EVT_DPI_CHANGED, [this](const DpiChangedEvent &evt) { this->Bind(EVT_DPI_CHANGED, [this](const DpiChangedEvent &evt) {
m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT; m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT;
m_new_font_point_size = get_default_font_for_dpi(evt.dpi).GetPointSize();
if (!m_can_rescale) if (!m_can_rescale)
return; return;
@ -124,6 +132,8 @@ private:
float m_prev_scale_factor; float m_prev_scale_factor;
bool m_can_rescale{ true }; bool m_can_rescale{ true };
int m_new_font_point_size;
// void recalc_font() // void recalc_font()
// { // {
// wxClientDC dc(this); // wxClientDC dc(this);
@ -135,14 +145,22 @@ private:
// check if new scale is differ from previous // check if new scale is differ from previous
bool is_new_scale_factor() const { return fabs(m_scale_factor - m_prev_scale_factor) > 0.001; } bool is_new_scale_factor() const { return fabs(m_scale_factor - m_prev_scale_factor) > 0.001; }
// function for a font scaling of the window
void scale_win_font(wxWindow *window, const int font_point_size)
{
wxFont new_font(window->GetFont());
new_font.SetPointSize(font_point_size);
window->SetFont(new_font);
}
// recursive function for scaling fonts for all controls in Window // recursive function for scaling fonts for all controls in Window
void scale_controls_fonts(wxWindow *window, const float scale_f) void scale_controls_fonts(wxWindow *window, const int font_point_size)
{ {
auto children = window->GetChildren(); auto children = window->GetChildren();
for (auto child : children) { for (auto child : children) {
scale_controls_fonts(child, scale_f); scale_controls_fonts(child, font_point_size);
child->SetFont(child->GetFont().Scaled(scale_f)); scale_win_font(child, font_point_size);
} }
window->Layout(); window->Layout();
@ -151,18 +169,18 @@ private:
void rescale(const wxRect &suggested_rect) void rescale(const wxRect &suggested_rect)
{ {
this->Freeze(); this->Freeze();
const float relative_scale_factor = m_scale_factor / m_prev_scale_factor;
// rescale fonts of all controls // rescale fonts of all controls
scale_controls_fonts(this, relative_scale_factor); scale_controls_fonts(this, m_new_font_point_size);
this->SetFont(this->GetFont().Scaled(relative_scale_factor)); // rescale current window font
scale_win_font(this, m_new_font_point_size);
// rescale normal_font value // set normal application font as a current window font
m_normal_font = m_normal_font.Scaled(relative_scale_factor); m_normal_font = this->GetFont();
// An analog of em_unit value from GUI_App. // update em_unit value for new window font
m_em_unit = std::max<size_t>(10, 10 * m_scale_factor); m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
// rescale missed controls sizes and images // rescale missed controls sizes and images
on_dpi_changed(suggested_rect); on_dpi_changed(suggested_rect);

View file

@ -39,10 +39,12 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
{ {
// Fonts were created by the DPIFrame constructor for the monitor, on which the window opened. // Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
wxGetApp().update_fonts(this); wxGetApp().update_fonts(this);
/*
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList #ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
this->SetFont(this->normal_font()); this->SetFont(this->normal_font());
#endif #endif
// Font is already set in DPIFrame constructor
*/
// Load the icon either from the exe, or from the ico file. // Load the icon either from the exe, or from the ico file.
#if _WIN32 #if _WIN32
{ {
@ -729,29 +731,26 @@ void MainFrame::quick_slice(const int qs)
// select input file // select input file
if (!(qs & qsReslice)) { if (!(qs & qsReslice)) {
auto dlg = new wxFileDialog(this, _(L("Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):")), wxFileDialog dlg(this, _(L("Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):")),
wxGetApp().app_config->get_last_dir(), "", wxGetApp().app_config->get_last_dir(), "",
file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg->ShowModal() != wxID_OK) { if (dlg.ShowModal() != wxID_OK)
dlg->Destroy();
return; return;
} input_file = dlg.GetPath();
input_file = dlg->GetPath();
dlg->Destroy();
if (!(qs & qsExportSVG)) if (!(qs & qsExportSVG))
m_qs_last_input_file = input_file; m_qs_last_input_file = input_file;
} }
else { else {
if (m_qs_last_input_file.IsEmpty()) { if (m_qs_last_input_file.IsEmpty()) {
auto dlg = new wxMessageDialog(this, _(L("No previously sliced file.")), wxMessageDialog dlg(this, _(L("No previously sliced file.")),
_(L("Error")), wxICON_ERROR | wxOK); _(L("Error")), wxICON_ERROR | wxOK);
dlg->ShowModal(); dlg.ShowModal();
return; return;
} }
if (std::ifstream(m_qs_last_input_file.ToUTF8().data())) { if (std::ifstream(m_qs_last_input_file.ToUTF8().data())) {
auto dlg = new wxMessageDialog(this, _(L("Previously sliced file ("))+m_qs_last_input_file+_(L(") not found.")), wxMessageDialog dlg(this, _(L("Previously sliced file ("))+m_qs_last_input_file+_(L(") not found.")),
_(L("File Not Found")), wxICON_ERROR | wxOK); _(L("File Not Found")), wxICON_ERROR | wxOK);
dlg->ShowModal(); dlg.ShowModal();
return; return;
} }
input_file = m_qs_last_input_file; input_file = m_qs_last_input_file;
@ -785,30 +784,24 @@ void MainFrame::quick_slice(const int qs)
} }
else if (qs & qsSaveAs) { else if (qs & qsSaveAs) {
// 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.
auto dlg = new wxFileDialog(this, wxString::Format(_(L("Save %s file as:")) , qs & qsExportSVG ? _(L("SVG")) : _(L("G-code")) ), wxFileDialog dlg(this, wxString::Format(_(L("Save %s file as:")) , qs & qsExportSVG ? _(L("SVG")) : _(L("G-code")) ),
wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), get_base_name(input_file), wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), get_base_name(input_file),
qs & qsExportSVG ? file_wildcards(FT_SVG) : file_wildcards(FT_GCODE), qs & qsExportSVG ? file_wildcards(FT_SVG) : file_wildcards(FT_GCODE),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT); wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (dlg->ShowModal() != wxID_OK) { if (dlg.ShowModal() != wxID_OK)
dlg->Destroy();
return; return;
} output_file = dlg.GetPath();
output_file = dlg->GetPath();
dlg->Destroy();
if (!(qs & qsExportSVG)) if (!(qs & qsExportSVG))
m_qs_last_output_file = output_file; m_qs_last_output_file = output_file;
wxGetApp().app_config->update_last_output_dir(get_dir_name(output_file)); wxGetApp().app_config->update_last_output_dir(get_dir_name(output_file));
} }
else if (qs & qsExportPNG) { else if (qs & qsExportPNG) {
auto dlg = new wxFileDialog(this, _(L("Save zip file as:")), wxFileDialog dlg(this, _(L("Save zip file as:")),
wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)),
get_base_name(output_file), "*.sl1", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); get_base_name(output_file), "*.sl1", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (dlg->ShowModal() != wxID_OK) { if (dlg.ShowModal() != wxID_OK)
dlg->Destroy();
return; return;
} output_file = dlg.GetPath();
output_file = dlg->GetPath();
dlg->Destroy();
} }
// show processbar dialog // show processbar dialog
@ -854,28 +847,22 @@ void MainFrame::repair_stl()
{ {
wxString input_file; wxString input_file;
{ {
auto dlg = new wxFileDialog(this, _(L("Select the STL file to repair:")), wxFileDialog dlg(this, _(L("Select the STL file to repair:")),
wxGetApp().app_config->get_last_dir(), "", wxGetApp().app_config->get_last_dir(), "",
file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST); file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg->ShowModal() != wxID_OK) { if (dlg.ShowModal() != wxID_OK)
dlg->Destroy();
return; return;
} input_file = dlg.GetPath();
input_file = dlg->GetPath();
dlg->Destroy();
} }
wxString output_file = input_file; wxString output_file = input_file;
{ {
auto dlg = new wxFileDialog( this, L("Save OBJ file (less prone to coordinate errors than STL) as:"), wxFileDialog dlg( this, L("Save OBJ file (less prone to coordinate errors than STL) as:"),
get_dir_name(output_file), get_base_name(output_file, ".obj"), get_dir_name(output_file), get_base_name(output_file, ".obj"),
file_wildcards(FT_OBJ), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); file_wildcards(FT_OBJ), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (dlg->ShowModal() != wxID_OK) { if (dlg.ShowModal() != wxID_OK)
dlg->Destroy();
return; return;
} output_file = dlg.GetPath();
output_file = dlg->GetPath();
dlg->Destroy();
} }
auto tmesh = new Slic3r::TriangleMesh(); auto tmesh = new Slic3r::TriangleMesh();
@ -896,14 +883,13 @@ void MainFrame::export_config()
return; return;
} }
// Ask user for the file name for the config file. // Ask user for the file name for the config file.
auto dlg = new wxFileDialog(this, _(L("Save configuration as:")), wxFileDialog dlg(this, _(L("Save configuration as:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
!m_last_config.IsEmpty() ? get_base_name(m_last_config) : "config.ini", !m_last_config.IsEmpty() ? get_base_name(m_last_config) : "config.ini",
file_wildcards(FT_INI), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); file_wildcards(FT_INI), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
wxString file; wxString file;
if (dlg->ShowModal() == wxID_OK) if (dlg.ShowModal() == wxID_OK)
file = dlg->GetPath(); file = dlg.GetPath();
dlg->Destroy();
if (!file.IsEmpty()) { if (!file.IsEmpty()) {
wxGetApp().app_config->update_config_dir(get_dir_name(file)); wxGetApp().app_config->update_config_dir(get_dir_name(file));
m_last_config = file; m_last_config = file;
@ -916,13 +902,12 @@ void MainFrame::load_config_file()
{ {
if (!wxGetApp().check_unsaved_changes()) if (!wxGetApp().check_unsaved_changes())
return; return;
auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")), wxFileDialog dlg(this, _(L("Select configuration to load:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
"config.ini", "INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g", wxFD_OPEN | wxFD_FILE_MUST_EXIST); "config.ini", "INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
wxString file; wxString file;
if (dlg->ShowModal() == wxID_OK) if (dlg.ShowModal() == wxID_OK)
file = dlg->GetPath(); file = dlg.GetPath();
dlg->Destroy();
if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) { if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) {
wxGetApp().app_config->update_config_dir(get_dir_name(file)); wxGetApp().app_config->update_config_dir(get_dir_name(file));
m_last_config = file; m_last_config = file;
@ -953,14 +938,13 @@ void MainFrame::export_configbundle()
return; return;
} }
// Ask user for a file name. // Ask user for a file name.
auto dlg = new wxFileDialog(this, _(L("Save presets bundle as:")), wxFileDialog dlg(this, _(L("Save presets bundle as:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
SLIC3R_APP_KEY "_config_bundle.ini", SLIC3R_APP_KEY "_config_bundle.ini",
file_wildcards(FT_INI), wxFD_SAVE | wxFD_OVERWRITE_PROMPT); file_wildcards(FT_INI), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
wxString file; wxString file;
if (dlg->ShowModal() == wxID_OK) if (dlg.ShowModal() == wxID_OK)
file = dlg->GetPath(); file = dlg.GetPath();
dlg->Destroy();
if (!file.IsEmpty()) { if (!file.IsEmpty()) {
// Export the config bundle. // Export the config bundle.
wxGetApp().app_config->update_config_dir(get_dir_name(file)); wxGetApp().app_config->update_config_dir(get_dir_name(file));
@ -980,15 +964,12 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re
if (!wxGetApp().check_unsaved_changes()) if (!wxGetApp().check_unsaved_changes())
return; return;
if (file.IsEmpty()) { if (file.IsEmpty()) {
auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")), wxFileDialog dlg(this, _(L("Select configuration to load:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
"config.ini", file_wildcards(FT_INI), wxFD_OPEN | wxFD_FILE_MUST_EXIST); "config.ini", file_wildcards(FT_INI), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg->ShowModal() != wxID_OK) { if (dlg.ShowModal() != wxID_OK)
dlg->Destroy(); return;
return; file = dlg.GetPath();
}
file = dlg->GetPath();
dlg->Destroy();
} }
wxGetApp().app_config->update_config_dir(get_dir_name(file)); wxGetApp().app_config->update_config_dir(get_dir_name(file));

View file

@ -1,4 +1,4 @@
#ifndef slic3r_MainFrame_hpp_ #ifndef slic3r_MainFrame_hpp_
#define slic3r_MainFrame_hpp_ #define slic3r_MainFrame_hpp_
#include "libslic3r/PrintConfig.hpp" #include "libslic3r/PrintConfig.hpp"

View file

@ -8,8 +8,6 @@
#include <wx/font.h> #include <wx/font.h>
#include <wx/bitmap.h> #include <wx/bitmap.h>
#include "slic3r/Utils/Semver.hpp"
class wxBoxSizer; class wxBoxSizer;
class wxCheckBox; class wxCheckBox;
class wxStaticBitmap; class wxStaticBitmap;

View file

@ -1,4 +1,4 @@
#include "OptionsGroup.hpp" #include "OptionsGroup.hpp"
#include "ConfigExceptions.hpp" #include "ConfigExceptions.hpp"
#include <utility> #include <utility>

View file

@ -290,17 +290,17 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 *
auto colors = static_cast<ConfigOptionStrings*>(cfg->option("extruder_colour")->clone()); auto colors = static_cast<ConfigOptionStrings*>(cfg->option("extruder_colour")->clone());
wxColour clr(colors->values[extruder_idx]); wxColour clr(colors->values[extruder_idx]);
if (!clr.IsOk()) if (!clr.IsOk())
clr = wxTransparentColour; clr = wxColour(0,0,0); // Don't set alfa to transparence
auto data = new wxColourData(); auto data = new wxColourData();
data->SetChooseFull(1); data->SetChooseFull(1);
data->SetColour(clr); data->SetColour(clr);
auto dialog = new wxColourDialog(this, data); wxColourDialog dialog(this, data);
dialog->CenterOnParent(); dialog.CenterOnParent();
if (dialog->ShowModal() == wxID_OK) if (dialog.ShowModal() == wxID_OK)
{ {
colors->values[extruder_idx] = dialog->GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX); colors->values[extruder_idx] = dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX);
DynamicPrintConfig cfg_new = *cfg; DynamicPrintConfig cfg_new = *cfg;
cfg_new.set_key_value("extruder_colour", colors); cfg_new.set_key_value("extruder_colour", colors);
@ -309,7 +309,6 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 *
wxGetApp().preset_bundle->update_platter_filament_ui(extruder_idx, this); wxGetApp().preset_bundle->update_platter_filament_ui(extruder_idx, this);
wxGetApp().plater()->on_config_change(cfg_new); wxGetApp().plater()->on_config_change(cfg_new);
} }
dialog->Destroy();
}); });
} }
@ -2511,15 +2510,14 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
default: break; default: break;
} }
wxFileDialog* dlg = new wxFileDialog(q, dlg_title, wxFileDialog dlg(q, dlg_title,
from_path(output_file.parent_path()), from_path(output_file.filename()), from_path(output_file.parent_path()), from_path(output_file.filename()),
wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (dlg->ShowModal() != wxID_OK) { if (dlg.ShowModal() != wxID_OK)
return wxEmptyString; return wxEmptyString;
}
wxString out_path = dlg->GetPath(); wxString out_path = dlg.GetPath();
fs::path path(into_path(out_path)); fs::path path(into_path(out_path));
wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); wxGetApp().app_config->update_last_output_dir(path.parent_path().string());
@ -4604,6 +4602,30 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
bool update_scheduled = false; bool update_scheduled = false;
bool bed_shape_changed = false; bool bed_shape_changed = false;
for (auto opt_key : p->config->diff(config)) { for (auto opt_key : p->config->diff(config)) {
if (opt_key == "filament_colour")
{
update_scheduled = true; // update should be scheduled (for update 3DScene) #2738
/* There is a case, when we use filament_color instead of extruder_color (when extruder_color == "").
* Thus plater config option "filament_colour" should be filled with filament_presets values.
* Otherwise, on 3dScene will be used last edited filament color for all volumes with extruder_color == "".
*/
const std::vector<std::string> filament_presets = wxGetApp().preset_bundle->filament_presets;
if (filament_presets.size() > 1 &&
p->config->option<ConfigOptionStrings>(opt_key)->values.size() != config.option<ConfigOptionStrings>(opt_key)->values.size())
{
const PresetCollection& filaments = wxGetApp().preset_bundle->filaments;
std::vector<std::string> filament_colors;
filament_colors.reserve(filament_presets.size());
for (const std::string& filament_preset : filament_presets)
filament_colors.push_back(filaments.find_preset(filament_preset, true)->config.opt_string("filament_colour", (unsigned)0));
p->config->option<ConfigOptionStrings>(opt_key)->values = filament_colors;
continue;
}
}
p->config->set_key_value(opt_key, config.option(opt_key)->clone()); p->config->set_key_value(opt_key, config.option(opt_key)->clone());
if (opt_key == "printer_technology") if (opt_key == "printer_technology")
this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key)); this->set_printer_technology(config.opt_enum<PrinterTechnology>(opt_key));

View file

@ -500,7 +500,8 @@ const std::vector<std::string>& Preset::sla_material_options()
if (s_opts.empty()) { if (s_opts.empty()) {
s_opts = { s_opts = {
"initial_layer_height", "initial_layer_height",
"exposure_time", "initial_exposure_time", "exposure_time",
"initial_exposure_time",
"material_correction", "material_correction",
"material_notes", "material_notes",
"default_sla_material_profile", "default_sla_material_profile",
@ -526,6 +527,8 @@ const std::vector<std::string>& Preset::sla_printer_options()
"relative_correction", "relative_correction",
"absolute_correction", "absolute_correction",
"gamma_correction", "gamma_correction",
"min_exposure_time", "max_exposure_time",
"min_initial_exposure_time", "max_initial_exposure_time",
"print_host", "printhost_apikey", "printhost_cafile", "print_host", "printhost_apikey", "printhost_cafile",
"printer_notes", "printer_notes",
"inherits" "inherits"

View file

@ -8,7 +8,7 @@
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
#include "libslic3r/PrintConfig.hpp" #include "libslic3r/PrintConfig.hpp"
#include "slic3r/Utils/Semver.hpp" #include "libslic3r/Semver.hpp"
class wxBitmap; class wxBitmap;
class wxBitmapComboBox; class wxBitmapComboBox;

View file

@ -763,8 +763,11 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
} }
} }
// Load the configs into this->filaments and make them active. // Load the configs into this->filaments and make them active.
this->filament_presets.clear(); this->filament_presets = std::vector<std::string>(configs.size());
for (size_t i = 0; i < configs.size(); ++ i) { // To avoid incorrect selection of the first filament preset (means a value of Preset->m_idx_selected)
// in a case when next added preset take a place of previosly selected preset,
// we should add presets from last to first
for (int i = (int)configs.size()-1; i >= 0; i--) {
DynamicPrintConfig &cfg = configs[i]; DynamicPrintConfig &cfg = configs[i];
// Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets. // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1]; cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1];
@ -789,7 +792,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
new_name, std::move(cfg), i == 0); new_name, std::move(cfg), i == 0);
loaded->save(); loaded->save();
} }
this->filament_presets.emplace_back(loaded->name); this->filament_presets[i] = loaded->name;
} }
} }
// 4) Load the project config values (the per extruder wipe matrix etc). // 4) Load the project config values (the per extruder wipe matrix etc).

View file

@ -159,8 +159,8 @@ void Tab::create_preset_tab()
m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_roll_back_value(true); })); m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) { on_roll_back_value(true); }));
m_question_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) m_question_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent)
{ {
auto dlg = new ButtonsDescription(this, m_icon_descriptions); ButtonsDescription dlg(this, m_icon_descriptions);
if (dlg->ShowModal() == wxID_OK) { if (dlg.ShowModal() == wxID_OK) {
// Colors for ui "decoration" // Colors for ui "decoration"
for (Tab *tab : wxGetApp().tabs_list) { for (Tab *tab : wxGetApp().tabs_list) {
tab->m_sys_label_clr = wxGetApp().get_label_clr_sys(); tab->m_sys_label_clr = wxGetApp().get_label_clr_sys();
@ -838,7 +838,7 @@ static wxString support_combo_value_for_config(const DynamicPrintConfig &config,
static wxString pad_combo_value_for_config(const DynamicPrintConfig &config) static wxString pad_combo_value_for_config(const DynamicPrintConfig &config)
{ {
return config.opt_bool("pad_enable") ? (config.opt_bool("pad_zero_elevation") ? _("Around object") : _("Below object")) : _("None"); return config.opt_bool("pad_enable") ? (config.opt_bool("pad_zero_elevation") ? _("Around object") : _("Below object")) : _("None");
} }
void Tab::on_value_change(const std::string& opt_key, const boost::any& value) void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
@ -860,8 +860,8 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
(opt_key == "supports_enable" || opt_key == "support_buildplate_only")) (opt_key == "supports_enable" || opt_key == "support_buildplate_only"))
og_freq_chng_params->set_value("support", support_combo_value_for_config(*m_config, is_fff)); og_freq_chng_params->set_value("support", support_combo_value_for_config(*m_config, is_fff));
if (! is_fff && (opt_key == "pad_enable" || opt_key == "pad_zero_elevation")) if (! is_fff && (opt_key == "pad_enable" || opt_key == "pad_zero_elevation"))
og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config)); og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
if (opt_key == "brim_width") if (opt_key == "brim_width")
{ {
@ -998,7 +998,7 @@ void Tab::update_frequently_changed_parameters()
og_freq_chng_params->set_value("support", support_combo_value_for_config(*m_config, is_fff)); og_freq_chng_params->set_value("support", support_combo_value_for_config(*m_config, is_fff));
if (! is_fff) if (! is_fff)
og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config)); og_freq_chng_params->set_value("pad", pad_combo_value_for_config(*m_config));
const std::string updated_value_key = is_fff ? "fill_density" : "pad_enable"; const std::string updated_value_key = is_fff ? "fill_density" : "pad_enable";
@ -1266,10 +1266,10 @@ void TabPrint::update()
if (m_config->opt_float("layer_height") < EPSILON) if (m_config->opt_float("layer_height") < EPSILON)
{ {
const wxString msg_text = _(L("Zero layer height is not valid.\n\nThe layer height will be reset to 0.01.")); const wxString msg_text = _(L("Zero layer height is not valid.\n\nThe layer height will be reset to 0.01."));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); wxMessageDialog dialog(parent(), msg_text, _(L("Layer height")), wxICON_WARNING | wxOK);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
is_msg_dlg_already_exist = true; is_msg_dlg_already_exist = true;
dialog->ShowModal(); dialog.ShowModal();
new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.01)); new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.01));
load_config(new_conf); load_config(new_conf);
is_msg_dlg_already_exist = false; is_msg_dlg_already_exist = false;
@ -1278,10 +1278,10 @@ void TabPrint::update()
if (fabs(m_config->option<ConfigOptionFloatOrPercent>("first_layer_height")->value - 0) < EPSILON) if (fabs(m_config->option<ConfigOptionFloatOrPercent>("first_layer_height")->value - 0) < EPSILON)
{ {
const wxString msg_text = _(L("Zero first layer height is not valid.\n\nThe first layer height will be reset to 0.01.")); const wxString msg_text = _(L("Zero first layer height is not valid.\n\nThe first layer height will be reset to 0.01."));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); wxMessageDialog dialog(parent(), msg_text, _(L("First layer height")), wxICON_WARNING | wxOK);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
is_msg_dlg_already_exist = true; is_msg_dlg_already_exist = true;
dialog->ShowModal(); dialog.ShowModal();
new_conf.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.01, false)); new_conf.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.01, false));
load_config(new_conf); load_config(new_conf);
is_msg_dlg_already_exist = false; is_msg_dlg_already_exist = false;
@ -1299,9 +1299,9 @@ void TabPrint::update()
"- no support material\n" "- no support material\n"
"- no ensure_vertical_shell_thickness\n" "- no ensure_vertical_shell_thickness\n"
"\nShall I adjust those settings in order to enable Spiral Vase?")); "\nShall I adjust those settings in order to enable Spiral Vase?"));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Spiral Vase")), wxICON_WARNING | wxYES | wxNO); wxMessageDialog dialog(parent(), msg_text, _(L("Spiral Vase")), wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_YES) { if (dialog.ShowModal() == wxID_YES) {
new_conf.set_key_value("perimeters", new ConfigOptionInt(1)); new_conf.set_key_value("perimeters", new ConfigOptionInt(1));
new_conf.set_key_value("top_solid_layers", new ConfigOptionInt(0)); new_conf.set_key_value("top_solid_layers", new ConfigOptionInt(0));
new_conf.set_key_value("fill_density", new ConfigOptionPercent(0)); new_conf.set_key_value("fill_density", new ConfigOptionPercent(0));
@ -1324,9 +1324,9 @@ void TabPrint::update()
"if they are printed with the current extruder without triggering a tool change.\n" "if they are printed with the current extruder without triggering a tool change.\n"
"(both support_material_extruder and support_material_interface_extruder need to be set to 0).\n" "(both support_material_extruder and support_material_interface_extruder need to be set to 0).\n"
"\nShall I adjust those settings in order to enable the Wipe Tower?")); "\nShall I adjust those settings in order to enable the Wipe Tower?"));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO); wxMessageDialog dialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_YES) { if (dialog.ShowModal() == wxID_YES) {
new_conf.set_key_value("support_material_extruder", new ConfigOptionInt(0)); new_conf.set_key_value("support_material_extruder", new ConfigOptionInt(0));
new_conf.set_key_value("support_material_interface_extruder", new ConfigOptionInt(0)); new_conf.set_key_value("support_material_interface_extruder", new ConfigOptionInt(0));
} }
@ -1341,9 +1341,9 @@ void TabPrint::update()
wxString msg_text = _(L("For the Wipe Tower to work with the soluble supports, the support layers\n" wxString msg_text = _(L("For the Wipe Tower to work with the soluble supports, the support layers\n"
"need to be synchronized with the object layers.\n" "need to be synchronized with the object layers.\n"
"\nShall I synchronize support layers in order to enable the Wipe Tower?")); "\nShall I synchronize support layers in order to enable the Wipe Tower?"));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO); wxMessageDialog dialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_YES) { if (dialog.ShowModal() == wxID_YES) {
new_conf.set_key_value("support_material_synchronize_layers", new ConfigOptionBool(true)); new_conf.set_key_value("support_material_synchronize_layers", new ConfigOptionBool(true));
} }
else else
@ -1359,9 +1359,9 @@ void TabPrint::update()
wxString msg_text = _(L("Supports work better, if the following feature is enabled:\n" wxString msg_text = _(L("Supports work better, if the following feature is enabled:\n"
"- Detect bridging perimeters\n" "- Detect bridging perimeters\n"
"\nShall I adjust those settings for supports?")); "\nShall I adjust those settings for supports?"));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Support Generator")), wxICON_WARNING | wxYES | wxNO | wxCANCEL); wxMessageDialog dialog(parent(), msg_text, _(L("Support Generator")), wxICON_WARNING | wxYES | wxNO | wxCANCEL);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
auto answer = dialog->ShowModal(); auto answer = dialog.ShowModal();
if (answer == wxID_YES) { if (answer == wxID_YES) {
// Enable "detect bridging perimeters". // Enable "detect bridging perimeters".
new_conf.set_key_value("overhangs", new ConfigOptionBool(true)); new_conf.set_key_value("overhangs", new ConfigOptionBool(true));
@ -1403,9 +1403,9 @@ void TabPrint::update()
if (!correct_100p_fill) { if (!correct_100p_fill) {
wxString msg_text = GUI::from_u8((boost::format(_utf8(L("The %1% infill pattern is not supposed to work at 100%% density.\n\n" wxString msg_text = GUI::from_u8((boost::format(_utf8(L("The %1% infill pattern is not supposed to work at 100%% density.\n\n"
"Shall I switch to rectilinear fill pattern?"))) % str_fill_pattern).str()); "Shall I switch to rectilinear fill pattern?"))) % str_fill_pattern).str());
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Infill")), wxICON_WARNING | wxYES | wxNO); wxMessageDialog dialog(parent(), msg_text, _(L("Infill")), wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_YES) { if (dialog.ShowModal() == wxID_YES) {
new_conf.set_key_value("fill_pattern", new ConfigOptionEnum<InfillPattern>(ipRectilinear)); new_conf.set_key_value("fill_pattern", new ConfigOptionEnum<InfillPattern>(ipRectilinear));
fill_density = 100; fill_density = 100;
} }
@ -1772,13 +1772,13 @@ void TabFilament::reload_config()
void TabFilament::update_volumetric_flow_preset_hints() void TabFilament::update_volumetric_flow_preset_hints()
{ {
wxString text; wxString text;
try { try {
text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle)); text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle));
} catch (std::exception &ex) { } catch (std::exception &ex) {
text = _(L("Volumetric flow hints not available\n\n")) + from_u8(ex.what()); text = _(L("Volumetric flow hints not available\n\n")) + from_u8(ex.what());
} }
m_volumetric_speed_description_line->SetText(text); m_volumetric_speed_description_line->SetText(text);
} }
void TabFilament::update() void TabFilament::update()
@ -1788,9 +1788,9 @@ void TabFilament::update()
m_update_cnt++; m_update_cnt++;
wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset())); wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset()));
m_cooling_description_line->SetText(text); m_cooling_description_line->SetText(text);
this->update_volumetric_flow_preset_hints(); this->update_volumetric_flow_preset_hints();
Layout(); Layout();
bool cooling = m_config->opt_bool("cooling", 0); bool cooling = m_config->opt_bool("cooling", 0);
@ -1812,8 +1812,8 @@ void TabFilament::update()
void TabFilament::OnActivate() void TabFilament::OnActivate()
{ {
this->update_volumetric_flow_preset_hints(); this->update_volumetric_flow_preset_hints();
Tab::OnActivate(); Tab::OnActivate();
} }
wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticText) wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticText)
@ -2045,10 +2045,10 @@ void TabPrinter::build_fff()
const wxString msg_text = _(L("Single Extruder Multi Material is selected, \n" const wxString msg_text = _(L("Single Extruder Multi Material is selected, \n"
"and all extruders must have the same diameter.\n" "and all extruders must have the same diameter.\n"
"Do you want to change the diameter for all extruders to first extruder nozzle diameter value?")); "Do you want to change the diameter for all extruders to first extruder nozzle diameter value?"));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO); wxMessageDialog dialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_YES) { if (dialog.ShowModal() == wxID_YES) {
for (size_t i = 1; i < nozzle_diameters.size(); i++) for (size_t i = 1; i < nozzle_diameters.size(); i++)
nozzle_diameters[i] = frst_diam; nozzle_diameters[i] = frst_diam;
@ -2290,6 +2290,12 @@ void TabPrinter::build_sla()
optgroup->append_single_option_line("absolute_correction"); optgroup->append_single_option_line("absolute_correction");
optgroup->append_single_option_line("gamma_correction"); optgroup->append_single_option_line("gamma_correction");
optgroup = page->new_optgroup(_(L("Exposure")));
optgroup->append_single_option_line("min_exposure_time");
optgroup->append_single_option_line("max_exposure_time");
optgroup->append_single_option_line("min_initial_exposure_time");
optgroup->append_single_option_line("max_initial_exposure_time");
optgroup = page->new_optgroup(_(L("Print Host upload"))); optgroup = page->new_optgroup(_(L("Print Host upload")));
build_printhost(optgroup.get()); build_printhost(optgroup.get());
@ -2507,10 +2513,10 @@ void TabPrinter::build_unregular_pages()
{ {
const wxString msg_text = _(L("This is a single extruder multimaterial printer, diameters of all extruders " const wxString msg_text = _(L("This is a single extruder multimaterial printer, diameters of all extruders "
"will be set to the new value. Do you want to proceed?")); "will be set to the new value. Do you want to proceed?"));
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO); wxMessageDialog dialog(parent(), msg_text, _(L("Nozzle diameter")), wxICON_WARNING | wxYES_NO);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_YES) { if (dialog.ShowModal() == wxID_YES) {
for (size_t i = 0; i < nozzle_diameters.size(); i++) { for (size_t i = 0; i < nozzle_diameters.size(); i++) {
if (i==extruder_idx) if (i==extruder_idx)
continue; continue;
@ -2558,7 +2564,33 @@ void TabPrinter::build_unregular_pages()
optgroup->append_single_option_line("retract_restart_extra_toolchange", extruder_idx); optgroup->append_single_option_line("retract_restart_extra_toolchange", extruder_idx);
optgroup = page->new_optgroup(_(L("Preview"))); optgroup = page->new_optgroup(_(L("Preview")));
optgroup->append_single_option_line("extruder_colour", extruder_idx);
auto reset_to_filament_color = [this, extruder_idx](wxWindow* parent) {
add_scaled_button(parent, &m_reset_to_filament_color, "undo",
_(L("Reset to Filament Color")), wxBU_LEFT | wxBU_EXACTFIT);
ScalableButton* btn = m_reset_to_filament_color;
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [this, extruder_idx](wxCommandEvent& e)
{
std::vector<std::string> colors = static_cast<const ConfigOptionStrings*>(m_config->option("extruder_colour"))->values;
colors[extruder_idx] = "";
DynamicPrintConfig new_conf = *m_config;
new_conf.set_key_value("extruder_colour", new ConfigOptionStrings(colors));
load_config(new_conf);
update_dirty();
update();
});
return sizer;
};
line = optgroup->create_single_option_line("extruder_colour", extruder_idx);
line.append_widget(reset_to_filament_color);
optgroup->append_line(line);
#ifdef __WXMSW__ #ifdef __WXMSW__
layout_page(page); layout_page(page);
@ -2715,13 +2747,13 @@ void TabPrinter::update_fff()
get_field("retract_before_wipe", i)->toggle(wipe); get_field("retract_before_wipe", i)->toggle(wipe);
if (use_firmware_retraction && wipe) { if (use_firmware_retraction && wipe) {
auto dialog = new wxMessageDialog(parent(), wxMessageDialog dialog(parent(),
_(L("The Wipe option is not available when using the Firmware Retraction mode.\n" _(L("The Wipe option is not available when using the Firmware Retraction mode.\n"
"\nShall I disable it in order to enable Firmware Retraction?")), "\nShall I disable it in order to enable Firmware Retraction?")),
_(L("Firmware Retraction")), wxICON_WARNING | wxYES | wxNO); _(L("Firmware Retraction")), wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_YES) { if (dialog.ShowModal() == wxID_YES) {
auto wipe = static_cast<ConfigOptionBools*>(m_config->option("wipe")->clone()); auto wipe = static_cast<ConfigOptionBools*>(m_config->option("wipe")->clone());
for (int w = 0; w < wipe->values.size(); w++) for (int w = 0; w < wipe->values.size(); w++)
wipe->values[w] = false; wipe->values[w] = false;
@ -3073,10 +3105,10 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr
message += wxString("\n") + tab + from_u8(new_printer_name) + "\n\n"; message += wxString("\n") + tab + from_u8(new_printer_name) + "\n\n";
message += _(L("and it has the following unsaved changes:")); message += _(L("and it has the following unsaved changes:"));
} }
auto confirm = new wxMessageDialog(parent(), wxMessageDialog confirm(parent(),
message + "\n" + changes + "\n\n" + _(L("Discard changes and continue anyway?")), message + "\n" + changes + "\n\n" + _(L("Discard changes and continue anyway?")),
_(L("Unsaved Changes")), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); _(L("Unsaved Changes")), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
return confirm->ShowModal() == wxID_YES; return confirm.ShowModal() == wxID_YES;
} }
// If we are switching from the FFF-preset to the SLA, we should to control the printed objects if they have a part(s). // If we are switching from the FFF-preset to the SLA, we should to control the printed objects if they have a part(s).
@ -3183,11 +3215,11 @@ void Tab::save_preset(std::string name /*= ""*/)
values.push_back(preset.name); values.push_back(preset.name);
} }
auto dlg = new SavePresetWindow(parent()); SavePresetWindow dlg(parent());
dlg->build(title(), default_name, values); dlg.build(title(), default_name, values);
if (dlg->ShowModal() != wxID_OK) if (dlg.ShowModal() != wxID_OK)
return; return;
name = dlg->get_name(); name = dlg.get_name();
if (name == "") { if (name == "") {
show_error(this, _(L("The supplied name is empty. It can't be saved."))); show_error(this, _(L("The supplied name is empty. It can't be saved.")));
return; return;
@ -3799,13 +3831,13 @@ void TabSLAPrint::update()
wxString msg_text = _( wxString msg_text = _(
L("Head penetration should not be greater than the head width.")); L("Head penetration should not be greater than the head width."));
auto dialog = new wxMessageDialog(parent(), wxMessageDialog dialog(parent(),
msg_text, msg_text,
_(L("Invalid Head penetration")), _(L("Invalid Head penetration")),
wxICON_WARNING | wxOK); wxICON_WARNING | wxOK);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_OK) { if (dialog.ShowModal() == wxID_OK) {
new_conf.set_key_value("support_head_penetration", new_conf.set_key_value("support_head_penetration",
new ConfigOptionFloat(head_width)); new ConfigOptionFloat(head_width));
} }
@ -3819,13 +3851,13 @@ void TabSLAPrint::update()
wxString msg_text = _(L( wxString msg_text = _(L(
"Pinhead diameter should be smaller than the pillar diameter.")); "Pinhead diameter should be smaller than the pillar diameter."));
auto dialog = new wxMessageDialog(parent(), wxMessageDialog dialog (parent(),
msg_text, msg_text,
_(L("Invalid pinhead diameter")), _(L("Invalid pinhead diameter")),
wxICON_WARNING | wxOK); wxICON_WARNING | wxOK);
DynamicPrintConfig new_conf = *m_config; DynamicPrintConfig new_conf = *m_config;
if (dialog->ShowModal() == wxID_OK) { if (dialog.ShowModal() == wxID_OK) {
new_conf.set_key_value("support_head_front_diameter", new_conf.set_key_value("support_head_front_diameter",
new ConfigOptionFloat(pillar_d / 2.0)); new ConfigOptionFloat(pillar_d / 2.0));
} }

View file

@ -371,6 +371,7 @@ public:
wxButton* m_serial_test_btn = nullptr; wxButton* m_serial_test_btn = nullptr;
ScalableButton* m_print_host_test_btn = nullptr; ScalableButton* m_print_host_test_btn = nullptr;
ScalableButton* m_printhost_browse_btn = nullptr; ScalableButton* m_printhost_browse_btn = nullptr;
ScalableButton* m_reset_to_filament_color = nullptr;
size_t m_extruders_count; size_t m_extruders_count;
size_t m_extruders_count_old = 0; size_t m_extruders_count_old = 0;

View file

@ -5,7 +5,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "slic3r/Utils/Semver.hpp" #include "libslic3r/Semver.hpp"
#include "MsgDialog.hpp" #include "MsgDialog.hpp"
class wxBoxSizer; class wxBoxSizer;

View file

@ -11,13 +11,14 @@
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/slider.h> #include <wx/slider.h>
#include <wx/menu.h> #include <wx/menu.h>
#include <wx/wx.h>
#include <vector> #include <vector>
#include <set> #include <set>
#include <functional> #include <functional>
namespace Slic3r { namespace Slic3r {
enum class ModelVolumeType : int; enum class ModelVolumeType : int;
}; };
typedef double coordf_t; typedef double coordf_t;
@ -36,11 +37,11 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const
std::function<void(wxCommandEvent& event)> cb, const std::string& icon = "", wxEvtHandler* event_handler = nullptr, std::function<void(wxCommandEvent& event)> cb, const std::string& icon = "", wxEvtHandler* event_handler = nullptr,
std::function<bool()> const cb_condition = []() { return true; }, wxWindow* parent = nullptr); std::function<bool()> const cb_condition = []() { return true; }, wxWindow* parent = nullptr);
wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description, wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description,
const std::string& icon = "", const std::string& icon = "",
std::function<bool()> const cb_condition = []() { return true; }, wxWindow* parent = nullptr); std::function<bool()> const cb_condition = []() { return true; }, wxWindow* parent = nullptr);
wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description, wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler); std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler);
wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description, wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
@ -51,7 +52,7 @@ void edit_tooltip(wxString& tooltip);
void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<int>& btn_ids); void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<int>& btn_ids);
int em_unit(wxWindow* win); int em_unit(wxWindow* win);
wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name, wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name,
const int px_cnt = 16, const bool is_horizontal = false, const bool grayscale = false); const int px_cnt = 16, const bool is_horizontal = false, const bool grayscale = false);
class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
@ -95,23 +96,23 @@ public:
class wxDataViewTreeCtrlComboPopup: public wxDataViewTreeCtrl, public wxComboPopup class wxDataViewTreeCtrlComboPopup: public wxDataViewTreeCtrl, public wxComboPopup
{ {
static const unsigned int DefaultWidth; static const unsigned int DefaultWidth;
static const unsigned int DefaultHeight; static const unsigned int DefaultHeight;
static const unsigned int DefaultItemHeight; static const unsigned int DefaultItemHeight;
wxString m_text; wxString m_text;
int m_cnt_open_items{0}; int m_cnt_open_items{0};
public: public:
virtual bool Create(wxWindow* parent); virtual bool Create(wxWindow* parent);
virtual wxWindow* GetControl() { return this; } virtual wxWindow* GetControl() { return this; }
virtual void SetStringValue(const wxString& value) { m_text = value; } virtual void SetStringValue(const wxString& value) { m_text = value; }
virtual wxString GetStringValue() const { return m_text; } virtual wxString GetStringValue() const { return m_text; }
// virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight); // virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight);
virtual void OnKeyEvent(wxKeyEvent& evt); virtual void OnKeyEvent(wxKeyEvent& evt);
void OnDataViewTreeCtrlSelection(wxCommandEvent& evt); void OnDataViewTreeCtrlSelection(wxCommandEvent& evt);
void SetItemsCnt(int cnt) { m_cnt_open_items = cnt; } void SetItemsCnt(int cnt) { m_cnt_open_items = cnt; }
}; };
@ -124,7 +125,7 @@ class DataViewBitmapText : public wxObject
public: public:
DataViewBitmapText( const wxString &text = wxEmptyString, DataViewBitmapText( const wxString &text = wxEmptyString,
const wxBitmap& bmp = wxNullBitmap) : const wxBitmap& bmp = wxNullBitmap) :
m_text(text), m_text(text),
m_bmp(bmp) m_bmp(bmp)
{ } { }
@ -195,8 +196,8 @@ WX_DEFINE_ARRAY_PTR(ObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray);
class ObjectDataViewModelNode class ObjectDataViewModelNode
{ {
ObjectDataViewModelNode* m_parent; ObjectDataViewModelNode* m_parent;
MyObjectTreeModelNodePtrArray m_children; MyObjectTreeModelNodePtrArray m_children;
wxBitmap m_empty_bmp; wxBitmap m_empty_bmp;
size_t m_volumes_cnt = 0; size_t m_volumes_cnt = 0;
std::vector< std::string > m_opt_categories; std::vector< std::string > m_opt_categories;
@ -216,7 +217,7 @@ class ObjectDataViewModelNode
Slic3r::ModelVolumeType m_volume_type; Slic3r::ModelVolumeType m_volume_type;
public: public:
ObjectDataViewModelNode(const wxString &name, ObjectDataViewModelNode(const wxString &name,
const wxString& extruder): const wxString& extruder):
m_parent(NULL), m_parent(NULL),
m_name(name), m_name(name),
@ -227,14 +228,14 @@ public:
init_container(); init_container();
} }
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const wxString& sub_obj_name, const wxString& sub_obj_name,
const wxBitmap& bmp, const wxBitmap& bmp,
const wxString& extruder, const wxString& extruder,
const int idx = -1 ) : const int idx = -1 ) :
m_parent (parent), m_parent (parent),
m_name (sub_obj_name), m_name (sub_obj_name),
m_type (itVolume), m_type (itVolume),
m_idx (idx), m_idx (idx),
m_extruder (extruder) m_extruder (extruder)
{ {
@ -243,27 +244,27 @@ public:
init_container(); init_container();
} }
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const t_layer_height_range& layer_range, const t_layer_height_range& layer_range,
const int idx = -1, const int idx = -1,
const wxString& extruder = wxEmptyString ); const wxString& extruder = wxEmptyString );
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type); ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type);
~ObjectDataViewModelNode() ~ObjectDataViewModelNode()
{ {
// free all our children nodes // free all our children nodes
size_t count = m_children.GetCount(); size_t count = m_children.GetCount();
for (size_t i = 0; i < count; i++) for (size_t i = 0; i < count; i++)
{ {
ObjectDataViewModelNode *child = m_children[i]; ObjectDataViewModelNode *child = m_children[i];
delete child; delete child;
} }
#ifndef NDEBUG #ifndef NDEBUG
// Indicate that the object was deleted. // Indicate that the object was deleted.
m_idx = -2; m_idx = -2;
#endif /* NDEBUG */ #endif /* NDEBUG */
} }
void init_container(); void init_container();
bool IsContainer() const bool IsContainer() const
@ -271,53 +272,53 @@ public:
return m_container; return m_container;
} }
ObjectDataViewModelNode* GetParent() ObjectDataViewModelNode* GetParent()
{ {
assert(m_parent == nullptr || m_parent->valid()); assert(m_parent == nullptr || m_parent->valid());
return m_parent; return m_parent;
} }
MyObjectTreeModelNodePtrArray& GetChildren() MyObjectTreeModelNodePtrArray& GetChildren()
{ {
return m_children; return m_children;
} }
ObjectDataViewModelNode* GetNthChild(unsigned int n) ObjectDataViewModelNode* GetNthChild(unsigned int n)
{ {
return m_children.Item(n); return m_children.Item(n);
} }
void Insert(ObjectDataViewModelNode* child, unsigned int n) void Insert(ObjectDataViewModelNode* child, unsigned int n)
{ {
if (!m_container) if (!m_container)
m_container = true; m_container = true;
m_children.Insert(child, n); m_children.Insert(child, n);
} }
void Append(ObjectDataViewModelNode* child) void Append(ObjectDataViewModelNode* child)
{ {
if (!m_container) if (!m_container)
m_container = true; m_container = true;
m_children.Add(child); m_children.Add(child);
} }
void RemoveAllChildren() void RemoveAllChildren()
{ {
if (GetChildCount() == 0) if (GetChildCount() == 0)
return; return;
for (int id = int(GetChildCount()) - 1; id >= 0; --id) for (int id = int(GetChildCount()) - 1; id >= 0; --id)
{ {
if (m_children.Item(id)->GetChildCount() > 0) if (m_children.Item(id)->GetChildCount() > 0)
m_children[id]->RemoveAllChildren(); m_children[id]->RemoveAllChildren();
auto node = m_children[id]; auto node = m_children[id];
m_children.RemoveAt(id); m_children.RemoveAt(id);
delete node; delete node;
} }
} }
size_t GetChildCount() const size_t GetChildCount() const
{ {
return m_children.GetCount(); return m_children.GetCount();
} }
bool SetValue(const wxVariant &variant, unsigned int col); bool SetValue(const wxVariant &variant, unsigned int col);
void SetBitmap(const wxBitmap &icon) { m_bmp = icon; } void SetBitmap(const wxBitmap &icon) { m_bmp = icon; }
const wxBitmap& GetBitmap() const { return m_bmp; } const wxBitmap& GetBitmap() const { return m_bmp; }
const wxString& GetName() const { return m_name; } const wxString& GetName() const { return m_name; }
ItemType GetType() const { return m_type; } ItemType GetType() const { return m_type; }
@ -326,46 +327,46 @@ public:
t_layer_height_range GetLayerRange() const { return m_layer_range; } t_layer_height_range GetLayerRange() const { return m_layer_range; }
PrintIndicator IsPrintable() const { return m_printable; } PrintIndicator IsPrintable() const { return m_printable; }
// use this function only for childrens // use this function only for childrens
void AssignAllVal(ObjectDataViewModelNode& from_node) void AssignAllVal(ObjectDataViewModelNode& from_node)
{ {
// ! Don't overwrite other values because of equality of this values for all children -- // ! Don't overwrite other values because of equality of this values for all children --
m_name = from_node.m_name; m_name = from_node.m_name;
m_bmp = from_node.m_bmp; m_bmp = from_node.m_bmp;
m_idx = from_node.m_idx; m_idx = from_node.m_idx;
m_extruder = from_node.m_extruder; m_extruder = from_node.m_extruder;
m_type = from_node.m_type; m_type = from_node.m_type;
} }
bool SwapChildrens(int frst_id, int scnd_id) { bool SwapChildrens(int frst_id, int scnd_id) {
if (GetChildCount() < 2 || if (GetChildCount() < 2 ||
frst_id < 0 || (size_t)frst_id >= GetChildCount() || frst_id < 0 || (size_t)frst_id >= GetChildCount() ||
scnd_id < 0 || (size_t)scnd_id >= GetChildCount()) scnd_id < 0 || (size_t)scnd_id >= GetChildCount())
return false; return false;
ObjectDataViewModelNode new_scnd = *GetNthChild(frst_id); ObjectDataViewModelNode new_scnd = *GetNthChild(frst_id);
ObjectDataViewModelNode new_frst = *GetNthChild(scnd_id); ObjectDataViewModelNode new_frst = *GetNthChild(scnd_id);
new_scnd.m_idx = m_children.Item(scnd_id)->m_idx; new_scnd.m_idx = m_children.Item(scnd_id)->m_idx;
new_frst.m_idx = m_children.Item(frst_id)->m_idx; new_frst.m_idx = m_children.Item(frst_id)->m_idx;
m_children.Item(frst_id)->AssignAllVal(new_frst); m_children.Item(frst_id)->AssignAllVal(new_frst);
m_children.Item(scnd_id)->AssignAllVal(new_scnd); m_children.Item(scnd_id)->AssignAllVal(new_scnd);
return true; return true;
} }
// Set action icons for node // Set action icons for node
void set_action_icon(); void set_action_icon();
// Set printable icon for node // Set printable icon for node
void set_printable_icon(PrintIndicator printable); void set_printable_icon(PrintIndicator printable);
void update_settings_digest_bitmaps(); void update_settings_digest_bitmaps();
bool update_settings_digest(const std::vector<std::string>& categories); bool update_settings_digest(const std::vector<std::string>& categories);
int volume_type() const { return int(m_volume_type); } int volume_type() const { return int(m_volume_type); }
void msw_rescale(); void msw_rescale();
#ifndef NDEBUG #ifndef NDEBUG
bool valid(); bool valid();
#endif /* NDEBUG */ #endif /* NDEBUG */
bool invalid() const { return m_idx < -1; } bool invalid() const { return m_idx < -1; }
@ -382,7 +383,7 @@ wxDECLARE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent);
class ObjectDataViewModel :public wxDataViewModel class ObjectDataViewModel :public wxDataViewModel
{ {
std::vector<ObjectDataViewModelNode*> m_objects; std::vector<ObjectDataViewModelNode*> m_objects;
std::vector<wxBitmap*> m_volume_bmps; std::vector<wxBitmap*> m_volume_bmps;
wxBitmap* m_warning_bmp; wxBitmap* m_warning_bmp;
@ -392,7 +393,7 @@ public:
ObjectDataViewModel(); ObjectDataViewModel();
~ObjectDataViewModel(); ~ObjectDataViewModel();
wxDataViewItem Add( const wxString &name, wxDataViewItem Add( const wxString &name,
const int extruder, const int extruder,
const bool has_errors = false); const bool has_errors = false);
wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item, wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item,
@ -405,24 +406,24 @@ public:
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num); wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector<bool>& print_indicator); wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector<bool>& print_indicator);
wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item); wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item);
wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item, wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item,
const t_layer_height_range& layer_range, const t_layer_height_range& layer_range,
const int extruder = 0, const int extruder = 0,
const int index = -1); const int index = -1);
wxDataViewItem Delete(const wxDataViewItem &item); wxDataViewItem Delete(const wxDataViewItem &item);
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num); wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
void DeleteAll(); void DeleteAll();
void DeleteChildren(wxDataViewItem& parent); void DeleteChildren(wxDataViewItem& parent);
void DeleteVolumeChildren(wxDataViewItem& parent); void DeleteVolumeChildren(wxDataViewItem& parent);
void DeleteSettings(const wxDataViewItem& parent); void DeleteSettings(const wxDataViewItem& parent);
wxDataViewItem GetItemById(int obj_idx); wxDataViewItem GetItemById(int obj_idx);
wxDataViewItem GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type); wxDataViewItem GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type);
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx); wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx); wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx);
wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range); wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range); int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
int GetIdByItem(const wxDataViewItem& item) const; int GetIdByItem(const wxDataViewItem& item) const;
int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const;
int GetObjectIdByItem(const wxDataViewItem& item) const; int GetObjectIdByItem(const wxDataViewItem& item) const;
int GetVolumeIdByItem(const wxDataViewItem& item) const; int GetVolumeIdByItem(const wxDataViewItem& item) const;
@ -433,53 +434,53 @@ public:
bool IsEmpty() { return m_objects.empty(); } bool IsEmpty() { return m_objects.empty(); }
bool InvalidItem(const wxDataViewItem& item); bool InvalidItem(const wxDataViewItem& item);
// helper method for wxLog // helper method for wxLog
wxString GetName(const wxDataViewItem &item) const; wxString GetName(const wxDataViewItem &item) const;
wxBitmap& GetBitmap(const wxDataViewItem &item) const; wxBitmap& GetBitmap(const wxDataViewItem &item) const;
// helper methods to change the model // helper methods to change the model
virtual unsigned int GetColumnCount() const override { return 3;} virtual unsigned int GetColumnCount() const override { return 3;}
virtual wxString GetColumnType(unsigned int col) const override{ return wxT("string"); } virtual wxString GetColumnType(unsigned int col) const override{ return wxT("string"); }
virtual void GetValue( wxVariant &variant, virtual void GetValue( wxVariant &variant,
const wxDataViewItem &item, const wxDataViewItem &item,
unsigned int col) const override; unsigned int col) const override;
virtual bool SetValue( const wxVariant &variant, virtual bool SetValue( const wxVariant &variant,
const wxDataViewItem &item, const wxDataViewItem &item,
unsigned int col) override; unsigned int col) override;
bool SetValue( const wxVariant &variant, bool SetValue( const wxVariant &variant,
const int item_idx, const int item_idx,
unsigned int col); unsigned int col);
// For parent move child from cur_volume_id place to new_volume_id // For parent move child from cur_volume_id place to new_volume_id
// Remaining items will moved up/down accordingly // Remaining items will moved up/down accordingly
wxDataViewItem ReorganizeChildren( const int cur_volume_id, wxDataViewItem ReorganizeChildren( const int cur_volume_id,
const int new_volume_id, const int new_volume_id,
const wxDataViewItem &parent); const wxDataViewItem &parent);
virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override; virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override;
virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override; virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override;
// get object item // get object item
wxDataViewItem GetTopParent(const wxDataViewItem &item) const; wxDataViewItem GetTopParent(const wxDataViewItem &item) const;
virtual bool IsContainer(const wxDataViewItem &item) const override; virtual bool IsContainer(const wxDataViewItem &item) const override;
virtual unsigned int GetChildren(const wxDataViewItem &parent, virtual unsigned int GetChildren(const wxDataViewItem &parent,
wxDataViewItemArray &array) const override; wxDataViewItemArray &array) const override;
void GetAllChildren(const wxDataViewItem &parent,wxDataViewItemArray &array) const; void GetAllChildren(const wxDataViewItem &parent,wxDataViewItemArray &array) const;
// Is the container just a header or an item with all columns // Is the container just a header or an item with all columns
// In our case it is an item with all columns // In our case it is an item with all columns
virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
ItemType GetItemType(const wxDataViewItem &item) const ; ItemType GetItemType(const wxDataViewItem &item) const ;
wxDataViewItem GetItemByType( const wxDataViewItem &parent_item, wxDataViewItem GetItemByType( const wxDataViewItem &parent_item,
ItemType type) const; ItemType type) const;
wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const; wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const;
wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const; wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const;
wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const; wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const;
bool IsSettingsItem(const wxDataViewItem &item) const; bool IsSettingsItem(const wxDataViewItem &item) const;
void UpdateSettingsDigest( const wxDataViewItem &item, void UpdateSettingsDigest( const wxDataViewItem &item,
const std::vector<std::string>& categories); const std::vector<std::string>& categories);
bool IsPrintable(const wxDataViewItem &item) const; bool IsPrintable(const wxDataViewItem &item) const;
@ -498,7 +499,7 @@ public:
// Rescale bitmaps for existing Items // Rescale bitmaps for existing Items
void Rescale(); void Rescale();
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
const bool is_marked = false); const bool is_marked = false);
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false); void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const; t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;
@ -547,12 +548,12 @@ public:
return false; return false;
#else #else
return true; return true;
#endif #endif
} }
wxWindow* CreateEditorCtrl(wxWindow* parent, wxWindow* CreateEditorCtrl(wxWindow* parent,
wxRect labelRect, wxRect labelRect,
const wxVariant& value) override; const wxVariant& value) override;
bool GetValueFromEditorCtrl( wxWindow* ctrl, bool GetValueFromEditorCtrl( wxWindow* ctrl,
wxVariant& value) override; wxVariant& value) override;
bool WasCanceled() const { return m_was_unusable_symbol; } bool WasCanceled() const { return m_was_unusable_symbol; }
@ -569,88 +570,88 @@ private:
class MyCustomRenderer : public wxDataViewCustomRenderer class MyCustomRenderer : public wxDataViewCustomRenderer
{ {
public: public:
// This renderer can be either activatable or editable, for demonstration // This renderer can be either activatable or editable, for demonstration
// purposes. In real programs, you should select whether the user should be // purposes. In real programs, you should select whether the user should be
// able to activate or edit the cell and it doesn't make sense to switch // able to activate or edit the cell and it doesn't make sense to switch
// between the two -- but this is just an example, so it doesn't stop us. // between the two -- but this is just an example, so it doesn't stop us.
explicit MyCustomRenderer(wxDataViewCellMode mode) explicit MyCustomRenderer(wxDataViewCellMode mode)
: wxDataViewCustomRenderer("string", mode, wxALIGN_CENTER) : wxDataViewCustomRenderer("string", mode, wxALIGN_CENTER)
{ } { }
virtual bool Render(wxRect rect, wxDC *dc, int state) override/*wxOVERRIDE*/ virtual bool Render(wxRect rect, wxDC *dc, int state) override/*wxOVERRIDE*/
{ {
dc->SetBrush(*wxLIGHT_GREY_BRUSH); dc->SetBrush(*wxLIGHT_GREY_BRUSH);
dc->SetPen(*wxTRANSPARENT_PEN); dc->SetPen(*wxTRANSPARENT_PEN);
rect.Deflate(2); rect.Deflate(2);
dc->DrawRoundedRectangle(rect, 5); dc->DrawRoundedRectangle(rect, 5);
RenderText(m_value, RenderText(m_value,
0, // no offset 0, // no offset
wxRect(dc->GetTextExtent(m_value)).CentreIn(rect), wxRect(dc->GetTextExtent(m_value)).CentreIn(rect),
dc, dc,
state); state);
return true; return true;
} }
virtual bool ActivateCell(const wxRect& WXUNUSED(cell), virtual bool ActivateCell(const wxRect& WXUNUSED(cell),
wxDataViewModel *WXUNUSED(model), wxDataViewModel *WXUNUSED(model),
const wxDataViewItem &WXUNUSED(item), const wxDataViewItem &WXUNUSED(item),
unsigned int WXUNUSED(col), unsigned int WXUNUSED(col),
const wxMouseEvent *mouseEvent) override/*wxOVERRIDE*/ const wxMouseEvent *mouseEvent) override/*wxOVERRIDE*/
{ {
wxString position; wxString position;
if (mouseEvent) if (mouseEvent)
position = wxString::Format("via mouse at %d, %d", mouseEvent->m_x, mouseEvent->m_y); position = wxString::Format("via mouse at %d, %d", mouseEvent->m_x, mouseEvent->m_y);
else else
position = "from keyboard"; position = "from keyboard";
// wxLogMessage("MyCustomRenderer ActivateCell() %s", position); // wxLogMessage("MyCustomRenderer ActivateCell() %s", position);
return false; return false;
} }
virtual wxSize GetSize() const override/*wxOVERRIDE*/ virtual wxSize GetSize() const override/*wxOVERRIDE*/
{ {
return wxSize(60, 20); return wxSize(60, 20);
} }
virtual bool SetValue(const wxVariant &value) override/*wxOVERRIDE*/ virtual bool SetValue(const wxVariant &value) override/*wxOVERRIDE*/
{ {
m_value = value.GetString(); m_value = value.GetString();
return true; return true;
} }
virtual bool GetValue(wxVariant &WXUNUSED(value)) const override/*wxOVERRIDE*/{ return true; } virtual bool GetValue(wxVariant &WXUNUSED(value)) const override/*wxOVERRIDE*/{ return true; }
virtual bool HasEditorCtrl() const override/*wxOVERRIDE*/{ return true; } virtual bool HasEditorCtrl() const override/*wxOVERRIDE*/{ return true; }
virtual wxWindow* virtual wxWindow*
CreateEditorCtrl(wxWindow* parent, CreateEditorCtrl(wxWindow* parent,
wxRect labelRect, wxRect labelRect,
const wxVariant& value) override/*wxOVERRIDE*/ const wxVariant& value) override/*wxOVERRIDE*/
{ {
wxTextCtrl* text = new wxTextCtrl(parent, wxID_ANY, value, wxTextCtrl* text = new wxTextCtrl(parent, wxID_ANY, value,
labelRect.GetPosition(), labelRect.GetPosition(),
labelRect.GetSize(), labelRect.GetSize(),
wxTE_PROCESS_ENTER); wxTE_PROCESS_ENTER);
text->SetInsertionPointEnd(); text->SetInsertionPointEnd();
return text; return text;
} }
virtual bool virtual bool
GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override/*wxOVERRIDE*/ GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override/*wxOVERRIDE*/
{ {
wxTextCtrl* text = wxDynamicCast(ctrl, wxTextCtrl); wxTextCtrl* text = wxDynamicCast(ctrl, wxTextCtrl);
if (!text) if (!text)
return false; return false;
value = text->GetValue(); value = text->GetValue();
return true; return true;
} }
private: private:
wxString m_value; wxString m_value;
}; };
@ -662,7 +663,7 @@ class ScalableBitmap
{ {
public: public:
ScalableBitmap() {}; ScalableBitmap() {};
ScalableBitmap( wxWindow *parent, ScalableBitmap( wxWindow *parent,
const std::string& icon_name = "", const std::string& icon_name = "",
const int px_cnt = 16, const int px_cnt = 16,
const bool is_horizontal = false); const bool is_horizontal = false);
@ -708,9 +709,9 @@ public:
DoubleSlider( DoubleSlider(
wxWindow *parent, wxWindow *parent,
wxWindowID id, wxWindowID id,
int lowerValue, int lowerValue,
int higherValue, int higherValue,
int minValue, int minValue,
int maxValue, int maxValue,
const wxPoint& pos = wxDefaultPosition, const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize, const wxSize& size = wxDefaultSize,
@ -753,8 +754,8 @@ public:
EnableTickManipulation(false); EnableTickManipulation(false);
} }
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
bool is_one_layer() const { return m_is_one_layer; } bool is_one_layer() const { return m_is_one_layer; }
bool is_lower_at_min() const { return m_lower_value == m_min_value; } bool is_lower_at_min() const { return m_lower_value == m_min_value; }
bool is_higher_at_max() const { return m_higher_value == m_max_value; } bool is_higher_at_max() const { return m_higher_value == m_max_value; }
bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); }
@ -773,7 +774,7 @@ public:
void OnRightUp(wxMouseEvent& event); void OnRightUp(wxMouseEvent& event);
protected: protected:
void render(); void render();
void draw_focus_rect(); void draw_focus_rect();
void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end); void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end);

View file

@ -5,6 +5,7 @@
#include <thread> #include <thread>
#include <deque> #include <deque>
#include <sstream> #include <sstream>
#include <exception>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
@ -165,7 +166,7 @@ size_t Http::priv::form_file_read_cb(char *buffer, size_t size, size_t nitems, v
try { try {
stream->read(buffer, size * nitems); stream->read(buffer, size * nitems);
} catch (...) { } catch (const std::exception &) {
return CURL_READFUNC_ABORT; return CURL_READFUNC_ABORT;
} }

View file

@ -2,6 +2,7 @@
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
#include <exception>
#include <boost/format.hpp> #include <boost/format.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
@ -69,7 +70,7 @@ bool OctoPrint::test(wxString &msg) const
msg = wxString::Format(_(L("Mismatched type of print host: %s")), text ? *text : "OctoPrint"); msg = wxString::Format(_(L("Mismatched type of print host: %s")), text ? *text : "OctoPrint");
} }
} }
catch (...) { catch (const std::exception &) {
res = false; res = false;
msg = "Could not parse server response"; msg = "Could not parse server response";
} }

View file

@ -35,6 +35,10 @@ using Slic3r::GUI::Config::Snapshot;
using Slic3r::GUI::Config::SnapshotDB; using Slic3r::GUI::Config::SnapshotDB;
// FIXME: Incompat bundle resolution doesn't deal with inherited user presets
namespace Slic3r { namespace Slic3r {
@ -101,6 +105,17 @@ struct Incompat
, vendor(std::move(vendor)) , vendor(std::move(vendor))
{} {}
void remove() {
// Remove the bundle file
fs::remove(bundle);
// Look for an installed index and remove it too if any
const fs::path installed_idx = bundle.replace_extension("idx");
if (fs::exists(installed_idx)) {
fs::remove(installed_idx);
}
}
friend std::ostream& operator<<(std::ostream& os , const Incompat &self) { friend std::ostream& operator<<(std::ostream& os , const Incompat &self) {
os << "Incompat(" << self.bundle.string() << ')'; os << "Incompat(" << self.bundle.string() << ')';
return os; return os;
@ -113,25 +128,12 @@ struct Updates
std::vector<Update> updates; std::vector<Update> updates;
}; };
static Semver get_slic3r_version()
{
auto res = Semver::parse(SLIC3R_VERSION);
if (! res) {
const char *error = "Could not parse Slic3r version string: " SLIC3R_VERSION;
BOOST_LOG_TRIVIAL(error) << error;
throw std::runtime_error(error);
}
return *res;
}
wxDEFINE_EVENT(EVT_SLIC3R_VERSION_ONLINE, wxCommandEvent); wxDEFINE_EVENT(EVT_SLIC3R_VERSION_ONLINE, wxCommandEvent);
struct PresetUpdater::priv struct PresetUpdater::priv
{ {
const Semver ver_slic3r;
std::vector<Index> index_db; std::vector<Index> index_db;
bool enabled_version_check; bool enabled_version_check;
@ -159,8 +161,7 @@ struct PresetUpdater::priv
}; };
PresetUpdater::priv::priv() PresetUpdater::priv::priv()
: ver_slic3r(get_slic3r_version()) : cache_path(fs::path(Slic3r::data_dir()) / "cache")
, cache_path(fs::path(Slic3r::data_dir()) / "cache")
, rsrc_path(fs::path(resources_dir()) / "profiles") , rsrc_path(fs::path(resources_dir()) / "profiles")
, vendor_path(fs::path(Slic3r::data_dir()) / "vendor") , vendor_path(fs::path(Slic3r::data_dir()) / "vendor")
, cancel(false) , cancel(false)
@ -383,25 +384,6 @@ Updates PresetUpdater::priv::get_config_updates() const
continue; continue;
} }
// Load 'installed' idx, if any.
// 'Installed' indices are kept alongside the bundle in the `vendor` subdir
// for bookkeeping to remember a cancelled update and not offer it again.
if (fs::exists(bundle_path_idx)) {
Index existing_idx;
try {
existing_idx.load(bundle_path_idx);
const auto existing_recommended = existing_idx.recommended();
if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) {
// The user has already seen (and presumably rejected) this update
BOOST_LOG_TRIVIAL(info) << boost::format("Downloaded index for `%1%` is the same as installed one, not offering an update.") % idx.vendor();
continue;
}
} catch (const std::exception & /* err */) {
BOOST_LOG_TRIVIAL(error) << boost::format("Could nto load installed index %1%") % bundle_path_idx;
}
}
const auto ver_current = idx.find(vp.config_version); const auto ver_current = idx.find(vp.config_version);
const bool ver_current_found = ver_current != idx.end(); const bool ver_current_found = ver_current != idx.end();
@ -424,6 +406,25 @@ Updates PresetUpdater::priv::get_config_updates() const
} else if (recommended->config_version > vp.config_version) { } else if (recommended->config_version > vp.config_version) {
// Config bundle update situation // Config bundle update situation
// Load 'installed' idx, if any.
// 'Installed' indices are kept alongside the bundle in the `vendor` subdir
// for bookkeeping to remember a cancelled update and not offer it again.
if (fs::exists(bundle_path_idx)) {
Index existing_idx;
try {
existing_idx.load(bundle_path_idx);
const auto existing_recommended = existing_idx.recommended();
if (existing_recommended != existing_idx.end() && recommended->config_version == existing_recommended->config_version) {
// The user has already seen (and presumably rejected) this update
BOOST_LOG_TRIVIAL(info) << boost::format("Downloaded index for `%1%` is the same as installed one, not offering an update.") % idx.vendor();
continue;
}
} catch (const std::exception &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("Could not load installed index at `%1%`: %2%") % bundle_path_idx % err.what();
}
}
// Check if the update is already present in a snapshot // Check if the update is already present in a snapshot
const auto recommended_snap = SnapshotDB::singleton().snapshot_with_vendor_preset(vp.name, recommended->config_version); const auto recommended_snap = SnapshotDB::singleton().snapshot_with_vendor_preset(vp.name, recommended->config_version);
if (recommended_snap != SnapshotDB::singleton().end()) { if (recommended_snap != SnapshotDB::singleton().end()) {
@ -485,12 +486,11 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% incompatible bundles") % updates.incompats.size(); BOOST_LOG_TRIVIAL(info) << boost::format("Deleting %1% incompatible bundles") % updates.incompats.size();
for (const auto &incompat : updates.incompats) { for (auto &incompat : updates.incompats) {
BOOST_LOG_TRIVIAL(info) << '\t' << incompat; BOOST_LOG_TRIVIAL(info) << '\t' << incompat;
fs::remove(incompat.bundle); incompat.remove();
} }
} } else if (updates.updates.size() > 0) {
else if (updates.updates.size() > 0) {
if (snapshot) { if (snapshot) {
BOOST_LOG_TRIVIAL(info) << "Taking a snapshot..."; BOOST_LOG_TRIVIAL(info) << "Taking a snapshot...";
SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE); SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE);
@ -584,8 +584,8 @@ void PresetUpdater::slic3r_update_notify()
if (ver_online) { if (ver_online) {
// Only display the notification if the version available online is newer AND if we haven't seen it before // Only display the notification if the version available online is newer AND if we haven't seen it before
if (*ver_online > p->ver_slic3r && (! ver_online_seen || *ver_online_seen < *ver_online)) { if (*ver_online > Slic3r::SEMVER && (! ver_online_seen || *ver_online_seen < *ver_online)) {
GUI::MsgUpdateSlic3r notification(p->ver_slic3r, *ver_online); GUI::MsgUpdateSlic3r notification(Slic3r::SEMVER, *ver_online);
notification.ShowModal(); notification.ShowModal();
if (notification.disable_version_check()) { if (notification.disable_version_check()) {
app_config->set("version_check", "0"); app_config->set("version_check", "0");
@ -628,11 +628,17 @@ PresetUpdater::UpdateResult PresetUpdater::config_update() const
const auto res = dlg.ShowModal(); const auto res = dlg.ShowModal();
if (res == wxID_REPLACE) { if (res == wxID_REPLACE) {
BOOST_LOG_TRIVIAL(info) << "User wants to re-configure..."; BOOST_LOG_TRIVIAL(info) << "User wants to re-configure...";
// This effectively removes the incompatible bundles:
// (snapshot is taken beforehand)
p->perform_updates(std::move(updates)); p->perform_updates(std::move(updates));
GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT); GUI::ConfigWizard wizard(nullptr, GUI::ConfigWizard::RR_DATA_INCOMPAT);
if (! wizard.run(GUI::wxGetApp().preset_bundle, this)) { if (! wizard.run(GUI::wxGetApp().preset_bundle, this)) {
return R_INCOMPAT_EXIT; return R_INCOMPAT_EXIT;
} }
GUI::wxGetApp().load_current_presets(); GUI::wxGetApp().load_current_presets();
return R_INCOMPAT_CONFIGURED; return R_INCOMPAT_CONFIGURED;
} else { } else {

View file

@ -170,8 +170,6 @@ void PrintHostJobQueue::priv::bg_thread_main()
} }
} catch (const std::exception &e) { } catch (const std::exception &e) {
emit_error(e.what()); emit_error(e.what());
} catch (...) {
emit_error("Unknown exception");
} }
// Cleanup leftover files, if any // Cleanup leftover files, if any

View file

@ -6,6 +6,7 @@
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <fstream> #include <fstream>
#include <exception>
#include <stdexcept> #include <stdexcept>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
@ -71,13 +72,10 @@ void parse_hardware_id(const std::string &hardware_id, SerialPortInfo &spi)
std::regex pattern("USB\\\\.*VID_([[:xdigit:]]+)&PID_([[:xdigit:]]+).*"); std::regex pattern("USB\\\\.*VID_([[:xdigit:]]+)&PID_([[:xdigit:]]+).*");
std::smatch matches; std::smatch matches;
if (std::regex_match(hardware_id, matches, pattern)) { if (std::regex_match(hardware_id, matches, pattern)) {
try { vid = std::stoul(matches[1].str(), 0, 16);
vid = std::stoul(matches[1].str(), 0, 16); pid = std::stoul(matches[2].str(), 0, 16);
pid = std::stoul(matches[2].str(), 0, 16); spi.id_vendor = vid;
spi.id_vendor = vid; spi.id_product = pid;
spi.id_product = pid;
}
catch (...) {}
} }
} }
#endif #endif

View file

@ -2,7 +2,7 @@
#define slic3r_Utils_Time_hpp_ #define slic3r_Utils_Time_hpp_
#include <string> #include <string>
#include <time.h> #include <ctime>
namespace Slic3r { namespace Slic3r {
namespace Utils { namespace Utils {