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

@ -52,8 +52,20 @@ 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)
@ -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 "")
@ -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"))
if (NOT MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall" ) 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)
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
if(MSVC)
# BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking. # 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 -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS) add_definitions(-DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601 )
endif() 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")

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>
@ -16,7 +17,7 @@ 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.
if(MSVC)
target_link_options(PrusaSlicer_app_gui PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>") 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

@ -61,7 +61,24 @@ PrinterTechnology get_printer_technology(const DynamicConfig &config)
int CLI::run(int argc, char **argv) int CLI::run(int argc, char **argv)
{ {
// Switch boost::filesystem to utf8. // Switch boost::filesystem to utf8.
try {
boost::nowide::nowide_filesystem(); 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;
@ -413,7 +430,7 @@ int CLI::run(int argc, char **argv)
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;
} }
@ -656,7 +673,7 @@ 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)
{ {

View file

@ -205,6 +205,8 @@ extern "C" {
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 */)
{ {
@ -298,3 +300,4 @@ int wmain(int argc, wchar_t **argv)
// 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)
if(MSVC)
target_include_directories(avrdude SYSTEM PRIVATE windows) # So that sources find the getopt.h windows drop-in 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,6 +63,11 @@ 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;
@ -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

@ -14,46 +14,42 @@
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; return false;
} }
for(int i = 0;i<THIS.data.size();i++) for(int i = 0;i<this->data.size();i++)
{ {
if(THIS.data(i) != that.data(i)) if(this->data(i) != that.data(i))
{ {
return false; return false;
} }
@ -64,7 +60,7 @@ namespace igl
{ {
return !(*this == that); return !(*this == that);
}; };
}; };
} }
#endif #endif

View file

@ -6,7 +6,7 @@
// 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,
@ -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);

View file

@ -30,6 +30,8 @@ IGL_INLINE bool igl::ray_mesh_intersect(
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
@ -254,27 +269,22 @@ protected:
} }
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,14 +517,42 @@ 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,

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
@ -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.
@ -475,7 +472,9 @@ 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);

View file

@ -123,10 +123,25 @@ public:
// 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; }
template<class Self> static auto tools_for_layer(Self& self, coordf_t print_z) -> decltype (*self.m_layer_tools.begin())
{
auto it_layer_tools = std::lower_bound(self.m_layer_tools.begin(), self.m_layer_tools.end(), LayerTools(print_z - EPSILON));
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;
}
// Find LayerTools with the closest print_z. // Find LayerTools with the closest print_z.
LayerTools& tools_for_layer(coordf_t print_z); LayerTools& tools_for_layer(coordf_t print_z) { return tools_for_layer(*this, print_z); }
const LayerTools& tools_for_layer(coordf_t print_z) const const LayerTools& tools_for_layer(coordf_t print_z) const { return tools_for_layer(*this, print_z); }
{ return *const_cast<const LayerTools*>(&const_cast<const ToolOrdering*>(this)->tools_for_layer(print_z)); }
const LayerTools& front() const { return m_layer_tools.front(); } const LayerTools& front() const { return m_layer_tools.front(); }
const LayerTools& back() const { return m_layer_tools.back(); } const LayerTools& back() const { return m_layer_tools.back(); }

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

@ -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>

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

@ -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;
} }
// borrowed from LVVM lib/Support/Windows/Path.inc
int rename_file(const std::string &from, const std::string &to)
{
int ec = 0;
#ifdef _WIN32 #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. // Convert to utf-16.
std::wstring wide_from = boost::nowide::widen(from); std::wstring wide_from = boost::nowide::widen(from);
std::wstring wide_to = boost::nowide::widen(to); std::wstring wide_to = boost::nowide::widen(to);
// Retry while we see recoverable errors. ScopedFileHandle from_handle;
// System scanners (eg. indexer) might open the source file when it is written // Retry this a few times to defeat badly behaved file system scanners.
// and closed. for (unsigned retry = 0; retry != 200; ++ retry) {
bool TryReplace = true; if (retry != 0)
::Sleep(10);
// This loop may take more than 2000 x 1ms to finish. 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);
for (int i = 0; i < 2000; ++ i) { if (from_handle)
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; break;
} }
if (::MoveFileExW(wide_from.c_str(), wide_to.c_str(), MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING)) if (! from_handle)
return 0; return map_windows_error(GetLastError());
DWORD MoveError = ::GetLastError();
ec = -1; // MoveError // We normally expect this loop to succeed after a few iterations. If it
if (MoveError != ERROR_ACCESS_DENIED && MoveError != ERROR_SHARING_VIOLATION) // 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; 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
std::error_code rename_file(const std::string &from, const std::string &to)
{
#ifdef _WIN32
return WindowsSupport::rename(from, to);
#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());
try {
pt::read_ini(ifs, tree); 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,6 +151,11 @@ 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 (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) { } catch (std::exception &ex) {
error = ex.what(); error = ex.what();
} catch (...) { } catch (...) {

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();
if (colour == wxTransparentColour)
m_value = std::string("");
else {
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue()); auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue());
m_value = clr_str.ToStdString(); 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

@ -135,6 +135,10 @@ ObjectList::ObjectList(wxWindow* parent) :
#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) {
return;
ctrl->msw_rescale(); ctrl->msw_rescale();
return;
}
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();
@ -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;
} }
@ -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,6 +11,7 @@
#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>

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,14 +72,11 @@ 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 {