Merge remote-tracking branch 'remotes/origin/dev_native'
24
Build.PL
|
@ -21,9 +21,7 @@ my %prereqs = qw(
|
|||
POSIX 0
|
||||
Scalar::Util 0
|
||||
Test::More 0
|
||||
Thread::Semaphore 0
|
||||
IO::Scalar 0
|
||||
threads 1.96
|
||||
Time::HiRes 0
|
||||
);
|
||||
my %recommends = qw(
|
||||
|
@ -32,22 +30,7 @@ my %recommends = qw(
|
|||
);
|
||||
|
||||
my $sudo = grep { $_ eq '--sudo' } @ARGV;
|
||||
my $gui = grep { $_ eq '--gui' } @ARGV;
|
||||
my $nolocal = grep { $_ eq '--nolocal' } @ARGV;
|
||||
if ($gui) {
|
||||
%prereqs = qw(
|
||||
Class::Accessor 0
|
||||
Wx 0.9918
|
||||
);
|
||||
%recommends = qw(
|
||||
Wx::GLCanvas 0
|
||||
);
|
||||
if ($^O eq 'MSWin32') {
|
||||
$recommends{"Win32::TieRegistry"} = 0;
|
||||
# we need an up-to-date Win32::API because older aren't thread-safe (GH #2517)
|
||||
$prereqs{'Win32::API'} = 0.79;
|
||||
}
|
||||
}
|
||||
|
||||
my @missing_prereqs = ();
|
||||
if ($ENV{SLIC3R_NO_AUTO}) {
|
||||
|
@ -133,13 +116,6 @@ EOF
|
|||
}
|
||||
}
|
||||
|
||||
print "\n";
|
||||
if ($gui) {
|
||||
print "Perl dependencies for the Slic3r GUI were installed.\n";
|
||||
} else {
|
||||
print "Perl dependencies for Slic3r were installed.\n";
|
||||
print "If you also want to use the GUI you can now run `perl Build.PL --gui` to install the required modules.\n";
|
||||
}
|
||||
print "\n";
|
||||
print "In the next step, you need to build the Slic3r C++ library.\n";
|
||||
print "1) Create a build directory and change to it\n";
|
||||
|
|
258
CMakeLists.txt
|
@ -1,6 +1,9 @@
|
|||
project(Slic3r)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
project(Slic3r)
|
||||
include("version.inc")
|
||||
set(SLIC3R_RESOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/resources")
|
||||
file(TO_NATIVE_PATH "${SLIC3R_RESOURCES_DIR}" SLIC3R_RESOURCES_DIR_WIN)
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
message(STATUS "No build type selected, default to Release")
|
||||
|
@ -19,36 +22,25 @@ endif()
|
|||
|
||||
option(SLIC3R_STATIC "Compile Slic3r with static libraries (Boost, TBB, glew)" ${SLIC3R_STATIC_INITIAL})
|
||||
option(SLIC3R_GUI "Compile Slic3r with GUI components (OpenGL, wxWidgets)" 1)
|
||||
option(SLIC3R_PRUSACONTROL "Compile Slic3r with the PrusaControl prject file format (requires wxWidgets base library)" 1)
|
||||
option(SLIC3R_PROFILE "Compile Slic3r with an invasive Shiny profiler" 0)
|
||||
option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
|
||||
option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
|
||||
option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0)
|
||||
option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0)
|
||||
|
||||
if (MSVC AND SLIC3R_MSVC_COMPILE_PARALLEL)
|
||||
add_compile_options(/MP)
|
||||
# Proposal for C++ unit tests and sandboxes
|
||||
option(SLIC3R_BUILD_SANDBOXES "Build development sandboxes" OFF)
|
||||
option(SLIC3R_BUILD_TESTS "Build unit tests" OFF)
|
||||
|
||||
if (MSVC)
|
||||
if (SLIC3R_MSVC_COMPILE_PARALLEL)
|
||||
add_compile_options(/MP)
|
||||
endif ()
|
||||
# /bigobj (Increase Number of Sections in .Obj file)
|
||||
# error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater
|
||||
add_compile_options(-bigobj -Zm316)
|
||||
endif ()
|
||||
|
||||
# Find the Perl interpreter, add local-lib to PATH and PERL5LIB environment variables,
|
||||
# so the locally installed modules (mainly the Alien::wxPerl) will be reached.
|
||||
if (WIN32)
|
||||
set(ENV_PATH_SEPARATOR ";")
|
||||
else()
|
||||
set(ENV_PATH_SEPARATOR ":")
|
||||
endif()
|
||||
set(ENV{PATH} "${PROJECT_SOURCE_DIR}/local-lib/bin${ENV_PATH_SEPARATOR}$ENV{PATH}")
|
||||
set(PERL_INCLUDE "${PROJECT_SOURCE_DIR}/local-lib/lib/perl5${ENV_PATH_SEPARATOR}$ENV{PERL5LIB}")
|
||||
message("PATH: $ENV{PATH}")
|
||||
message("PERL_INCLUDE: ${PERL_INCLUDE}")
|
||||
find_package(Perl REQUIRED)
|
||||
if (WIN32)
|
||||
# On Windows passing the PERL5LIB variable causes various problems (such as with MAX_PATH and others),
|
||||
# basically I've found no good way to do it on Windows.
|
||||
set(PERL5LIB_ENV_CMD "")
|
||||
else()
|
||||
set(PERL5LIB_ENV_CMD ${CMAKE_COMMAND} -E env PERL5LIB=${PERL_INCLUDE})
|
||||
endif()
|
||||
|
||||
|
||||
# CMAKE_PREFIX_PATH is used to point CMake to the remaining dependencies (Boost, TBB, ...)
|
||||
# We pick it from environment if it is not defined in another way
|
||||
if(NOT DEFINED CMAKE_PREFIX_PATH)
|
||||
|
@ -57,8 +49,21 @@ if(NOT DEFINED CMAKE_PREFIX_PATH)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# Add our own cmake module path.
|
||||
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/)
|
||||
|
||||
enable_testing ()
|
||||
|
||||
# Enable C++11 language standard.
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
||||
# To be able to link libslic3r with the Perl XS module.
|
||||
# Once we get rid of Perl and libslic3r is linked statically, we can get rid of -fPIC
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory.
|
||||
# We pick it from environment if it is not defined in another way
|
||||
if(WIN32)
|
||||
|
@ -73,28 +78,199 @@ if(WIN32)
|
|||
message("STL fixing by the Netfabb service will not be compiled")
|
||||
unset(WIN10SDK_PATH)
|
||||
endif()
|
||||
if(WIN10SDK_PATH)
|
||||
message("Building with Win10 Netfabb STL fixing service support")
|
||||
add_definitions(-DHAS_WIN10SDK)
|
||||
include_directories("${WIN10SDK_PATH}/Include")
|
||||
else()
|
||||
message("Building without Win10 Netfabb STL fixing service support")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_subdirectory(xs)
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# Workaround for an old CMake, which does not understand CMAKE_CXX_STANDARD.
|
||||
add_compile_options(-std=c++11 -Wall -Wno-reorder)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
endif()
|
||||
|
||||
get_filename_component(PERL_BIN_PATH "${PERL_EXECUTABLE}" DIRECTORY)
|
||||
if (MSVC)
|
||||
# By default the startup project in MSVC is the 'ALL_BUILD' cmake-created project,
|
||||
# but we want 'slic3r' as the startup one because debugging run command is associated with it.
|
||||
# (Unfortunatelly it cannot be associated with ALL_BUILD using CMake.)
|
||||
# Note: For some reason this needs to be set in the top-level CMakeLists.txt
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT XS)
|
||||
set(PERL_PROVE "${PERL_BIN_PATH}/prove.bat")
|
||||
else ()
|
||||
set(PERL_PROVE "${PERL_BIN_PATH}/prove")
|
||||
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUXX)
|
||||
# Adding -fext-numeric-literals to enable GCC extensions on definitions of quad float literals, which are required by Boost.
|
||||
add_compile_options(-fext-numeric-literals)
|
||||
endif()
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
# On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error.
|
||||
add_compile_options(-Werror=return-type)
|
||||
|
||||
if (SLIC3R_ASAN)
|
||||
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lasan")
|
||||
endif ()
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
# Where all the bundled libraries reside?
|
||||
set(LIBDIR ${CMAKE_CURRENT_SOURCE_DIR}/src/)
|
||||
# For the bundled boost libraries (boost::nowide)
|
||||
include_directories(${LIBDIR})
|
||||
# For libslic3r.h
|
||||
include_directories(${LIBDIR}/libslic3r ${LIBDIR}/clipper ${LIBDIR}/polypartition)
|
||||
#set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
if(WIN32)
|
||||
# BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking.
|
||||
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
add_definitions(-DwxUSE_UNICODE -D_UNICODE -DUNICODE -DWXINTL_NO_GETTEXT_MACRO)
|
||||
|
||||
if (SLIC3R_PROFILE)
|
||||
message("Slic3r will be built with a Shiny invasive profiler")
|
||||
add_definitions(-DSLIC3R_PROFILE)
|
||||
endif ()
|
||||
|
||||
add_test (NAME xs COMMAND "${PERL_EXECUTABLE}" ${PERL_PROVE} -I ${PROJECT_SOURCE_DIR}/local-lib/lib/perl5 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/xs)
|
||||
add_test (NAME integration COMMAND "${PERL_EXECUTABLE}" ${PERL_PROVE} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
# Disable optimization even with debugging on.
|
||||
if (0)
|
||||
message(STATUS "Perl compiled without optimization. Disabling optimization for the Slic3r build.")
|
||||
message("Old CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
message("Old CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
message("Old CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "/MD /Od /Zi /EHsc /DWIN32 /DTBB_USE_ASSERT")
|
||||
set(CMAKE_C_FLAGS_RELEASE "/MD /Od /Zi /DWIN32 /DTBB_USE_ASSERT")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /EHsc /DWIN32 /DTBB_USE_ASSERT")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "/MD /Od /Zi /DWIN32 /DTBB_USE_ASSERT")
|
||||
set(CMAKE_CXX_FLAGS "/MD /Od /Zi /EHsc /DWIN32 /DTBB_USE_ASSERT")
|
||||
set(CMAKE_C_FLAGS "/MD /Od /Zi /DWIN32 /DTBB_USE_ASSERT")
|
||||
endif()
|
||||
|
||||
install(PROGRAMS slic3r.pl DESTINATION bin RENAME slic3r-prusa3d)
|
||||
# Find and configure boost
|
||||
if(SLIC3R_STATIC)
|
||||
# Use static boost libraries.
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
# Use boost libraries linked statically to the C++ runtime.
|
||||
# set(Boost_USE_STATIC_RUNTIME ON)
|
||||
endif()
|
||||
#set(Boost_DEBUG ON)
|
||||
set(Boost_COMPILER "-vc120")
|
||||
find_package(Boost REQUIRED COMPONENTS system filesystem thread log locale regex)
|
||||
if(Boost_FOUND)
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
if (APPLE)
|
||||
# BOOST_ASIO_DISABLE_KQUEUE : prevents a Boost ASIO bug on OS X: https://svn.boost.org/trac/boost/ticket/5339
|
||||
add_definitions(-DBOOST_ASIO_DISABLE_KQUEUE)
|
||||
endif()
|
||||
if(NOT SLIC3R_STATIC)
|
||||
add_definitions(-DBOOST_LOG_DYN_LINK)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Find and configure intel-tbb
|
||||
if(SLIC3R_STATIC)
|
||||
set(TBB_STATIC 1)
|
||||
endif()
|
||||
set(TBB_DEBUG 1)
|
||||
find_package(TBB REQUIRED)
|
||||
include_directories(${TBB_INCLUDE_DIRS})
|
||||
add_definitions(${TBB_DEFINITIONS})
|
||||
if(MSVC)
|
||||
# Suppress implicit linking of the TBB libraries by the Visual Studio compiler.
|
||||
add_definitions(-D__TBB_NO_IMPLICIT_LINKAGE)
|
||||
endif()
|
||||
# The Intel TBB library will use the std::exception_ptr feature of C++11.
|
||||
add_definitions(-DTBB_USE_CAPTURED_EXCEPTION=0)
|
||||
|
||||
#set(CURL_DEBUG 1)
|
||||
find_package(CURL REQUIRED)
|
||||
include_directories(${CURL_INCLUDE_DIRS})
|
||||
|
||||
if (SLIC3R_STATIC)
|
||||
if (NOT APPLE)
|
||||
# libcurl is always linked dynamically to the system libcurl on OSX.
|
||||
# On other systems, libcurl is linked statically if SLIC3R_STATIC is set.
|
||||
add_definitions(-DCURL_STATICLIB)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# As of now, our build system produces a statically linked libcurl,
|
||||
# which links the OpenSSL library dynamically.
|
||||
find_package(OpenSSL REQUIRED)
|
||||
message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}")
|
||||
message("OpenSSL libraries: ${OPENSSL_LIBRARIES}")
|
||||
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
## OPTIONAL packages
|
||||
|
||||
# Find eigen3 or use bundled version
|
||||
if (NOT SLIC3R_STATIC)
|
||||
find_package(Eigen3)
|
||||
endif ()
|
||||
if (NOT Eigen3_FOUND)
|
||||
set(Eigen3_FOUND 1)
|
||||
set(EIGEN3_INCLUDE_DIR ${LIBDIR}/eigen/)
|
||||
endif ()
|
||||
include_directories(${EIGEN3_INCLUDE_DIR})
|
||||
|
||||
# Find expat or use bundled version
|
||||
# Always use the system libexpat on Linux.
|
||||
if (NOT SLIC3R_STATIC OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
find_package(EXPAT)
|
||||
endif ()
|
||||
if (NOT EXPAT_FOUND)
|
||||
add_library(expat STATIC
|
||||
${LIBDIR}/expat/xmlparse.c
|
||||
${LIBDIR}/expat/xmlrole.c
|
||||
${LIBDIR}/expat/xmltok.c
|
||||
)
|
||||
set(EXPAT_FOUND 1)
|
||||
set(EXPAT_INCLUDE_DIRS ${LIBDIR}/expat/)
|
||||
set(EXPAT_LIBRARIES expat)
|
||||
endif ()
|
||||
include_directories(${EXPAT_INCLUDE_DIRS})
|
||||
|
||||
# Find glew or use bundled version
|
||||
if (NOT SLIC3R_STATIC)
|
||||
find_package(GLEW)
|
||||
endif ()
|
||||
if (NOT GLEW_FOUND)
|
||||
add_library(glew STATIC ${LIBDIR}/glew/src/glew.c)
|
||||
set(GLEW_FOUND 1)
|
||||
set(GLEW_INCLUDE_DIRS ${LIBDIR}/glew/include/)
|
||||
set(GLEW_LIBRARIES glew)
|
||||
add_definitions(-DGLEW_STATIC)
|
||||
endif ()
|
||||
include_directories(${GLEW_INCLUDE_DIRS})
|
||||
|
||||
# l10n
|
||||
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")
|
||||
add_custom_target(pot
|
||||
# FIXME: file list stale
|
||||
COMMAND xgettext --keyword=L --from-code=UTF-8 --debug
|
||||
-f "${L10N_DIR}/list.txt"
|
||||
-o "${L10N_DIR}/Slic3rPE.pot"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
COMMENT "Generate pot file from strings in the source tree"
|
||||
)
|
||||
|
||||
# libslic3r, Slic3r GUI and the slic3r executable.
|
||||
add_subdirectory(src)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT slic3r)
|
||||
|
||||
# Perl bindings, currently only used for the unit / integration tests of libslic3r.
|
||||
# Also runs the unit / integration tests.
|
||||
#FIXME Port the tests into C++ to finally get rid of the Perl!
|
||||
if (SLIC3R_PERL_XS)
|
||||
add_subdirectory(xs)
|
||||
endif ()
|
||||
|
||||
if(SLIC3R_BUILD_SANDBOXES)
|
||||
add_subdirectory(sandboxes)
|
||||
endif()
|
||||
|
||||
if(SLIC3R_BUILD_TESTS)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
file(GLOB MyVar var/*.png)
|
||||
install(FILES ${MyVar} DESTINATION share/slic3r-prusa3d)
|
||||
install(FILES lib/Slic3r.pm DESTINATION lib/slic3r-prusa3d)
|
||||
install(DIRECTORY lib/Slic3r DESTINATION lib/slic3r-prusa3d)
|
||||
|
|
|
@ -127,7 +127,7 @@ The author of the Silk icon set is Mark James.
|
|||
and [input_filename] (default: [input_filename_base].gcode)
|
||||
--post-process Generated G-code will be processed with the supplied script;
|
||||
call this more than once to process through multiple scripts.
|
||||
--export-svg Export a SVG file containing slices instead of G-code.
|
||||
--export-png Export zipped PNG files containing slices instead of G-code.
|
||||
-m, --merge If multiple files are supplied, they will be composed into a single
|
||||
print rather than processed individually.
|
||||
|
||||
|
|
|
@ -5,34 +5,62 @@
|
|||
# FindCURL
|
||||
# --------
|
||||
#
|
||||
# Find curl
|
||||
#
|
||||
# Find the native CURL headers and libraries.
|
||||
#
|
||||
# ::
|
||||
# IMPORTED Targets
|
||||
# ^^^^^^^^^^^^^^^^
|
||||
#
|
||||
# CURL_INCLUDE_DIRS - where to find curl/curl.h, etc.
|
||||
# CURL_LIBRARIES - List of libraries when using curl.
|
||||
# CURL_FOUND - True if curl found.
|
||||
# CURL_VERSION_STRING - the version of curl found (since CMake 2.8.8)
|
||||
# This module defines :prop_tgt:`IMPORTED` target ``CURL::libcurl``, if
|
||||
# curl has been found.
|
||||
#
|
||||
# Result Variables
|
||||
# ^^^^^^^^^^^^^^^^
|
||||
#
|
||||
# This module defines the following variables:
|
||||
#
|
||||
# ``CURL_FOUND``
|
||||
# True if curl found.
|
||||
#
|
||||
# ``CURL_INCLUDE_DIRS``
|
||||
# where to find curl/curl.h, etc.
|
||||
#
|
||||
# ``CURL_LIBRARIES``
|
||||
# List of libraries when using curl.
|
||||
#
|
||||
# ``CURL_VERSION_STRING``
|
||||
# The version of curl found.
|
||||
|
||||
# Look for the header file.
|
||||
find_path(CURL_INCLUDE_DIR NAMES curl/curl.h)
|
||||
mark_as_advanced(CURL_INCLUDE_DIR)
|
||||
|
||||
# Look for the library (sorted from most current/relevant entry to least).
|
||||
find_library(CURL_LIBRARY NAMES
|
||||
curl
|
||||
# Windows MSVC Makefile:
|
||||
libcurl_a
|
||||
# Windows MSVC prebuilts:
|
||||
curllib
|
||||
libcurl_imp
|
||||
curllib_static
|
||||
# Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip):
|
||||
libcurl
|
||||
)
|
||||
mark_as_advanced(CURL_LIBRARY)
|
||||
if(NOT CURL_LIBRARY)
|
||||
# Look for the library (sorted from most current/relevant entry to least).
|
||||
find_library(CURL_LIBRARY_RELEASE NAMES
|
||||
curl
|
||||
# Windows MSVC prebuilts:
|
||||
curllib
|
||||
libcurl_imp
|
||||
curllib_static
|
||||
# Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip):
|
||||
libcurl
|
||||
# Static library on Windows
|
||||
libcurl_a
|
||||
)
|
||||
mark_as_advanced(CURL_LIBRARY_RELEASE)
|
||||
|
||||
find_library(CURL_LIBRARY_DEBUG NAMES
|
||||
# Windows MSVC CMake builds in debug configuration on vcpkg:
|
||||
libcurl-d_imp
|
||||
libcurl-d
|
||||
# Static library on Windows, compiled in debug mode
|
||||
libcurl_a_debug
|
||||
)
|
||||
mark_as_advanced(CURL_LIBRARY_DEBUG)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/SelectLibraryConfigurations_SLIC3R.cmake)
|
||||
select_library_configurations_SLIC3R(CURL)
|
||||
endif()
|
||||
|
||||
if(CURL_INCLUDE_DIR)
|
||||
foreach(_curl_version_header curlver.h curl.h)
|
||||
|
@ -46,7 +74,8 @@ if(CURL_INCLUDE_DIR)
|
|||
endforeach()
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(CURL
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs_SLIC3R.cmake)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS_SLIC3R(CURL
|
||||
REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR
|
||||
VERSION_VAR CURL_VERSION_STRING)
|
||||
|
||||
|
@ -54,6 +83,29 @@ if(CURL_FOUND)
|
|||
set(CURL_LIBRARIES ${CURL_LIBRARY})
|
||||
set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
|
||||
|
||||
message(STATUS " Curl libraries: = ${CURL_LIBRARIES}")
|
||||
message(STATUS " Curl include dirs: = ${CURL_INCLUDE_DIRS}")
|
||||
if(NOT TARGET CURL::libcurl)
|
||||
add_library(CURL::libcurl UNKNOWN IMPORTED)
|
||||
set_target_properties(CURL::libcurl PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}")
|
||||
|
||||
if(EXISTS "${CURL_LIBRARY}")
|
||||
set_target_properties(CURL::libcurl PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${CURL_LIBRARY}")
|
||||
endif()
|
||||
if(CURL_LIBRARY_RELEASE)
|
||||
set_property(TARGET CURL::libcurl APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS RELEASE)
|
||||
set_target_properties(CURL::libcurl PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION_RELEASE "${CURL_LIBRARY_RELEASE}")
|
||||
endif()
|
||||
if(CURL_LIBRARY_DEBUG)
|
||||
set_property(TARGET CURL::libcurl APPEND PROPERTY
|
||||
IMPORTED_CONFIGURATIONS DEBUG)
|
||||
set_target_properties(CURL::libcurl PROPERTIES
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION_DEBUG "${CURL_LIBRARY_DEBUG}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
392
cmake/modules/FindPackageHandleStandardArgs_SLIC3R.cmake
Normal file
|
@ -0,0 +1,392 @@
|
|||
# Modified from the CMake github master,
|
||||
# required by the bundled FindCURL.cmake
|
||||
|
||||
|
||||
|
||||
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindPackageHandleStandardArgs
|
||||
-----------------------------
|
||||
|
||||
This module provides a function intended to be used in :ref:`Find Modules`
|
||||
implementing :command:`find_package(<PackageName>)` calls. It handles the
|
||||
``REQUIRED``, ``QUIET`` and version-related arguments of ``find_package``.
|
||||
It also sets the ``<PackageName>_FOUND`` variable. The package is
|
||||
considered found if all variables listed contain valid results, e.g.
|
||||
valid filepaths.
|
||||
|
||||
.. command:: find_package_handle_standard_args
|
||||
|
||||
There are two signatures::
|
||||
|
||||
find_package_handle_standard_args(<PackageName>
|
||||
(DEFAULT_MSG|<custom-failure-message>)
|
||||
<required-var>...
|
||||
)
|
||||
|
||||
find_package_handle_standard_args(<PackageName>
|
||||
[FOUND_VAR <result-var>]
|
||||
[REQUIRED_VARS <required-var>...]
|
||||
[VERSION_VAR <version-var>]
|
||||
[HANDLE_COMPONENTS]
|
||||
[CONFIG_MODE]
|
||||
[FAIL_MESSAGE <custom-failure-message>]
|
||||
)
|
||||
|
||||
The ``<PackageName>_FOUND`` variable will be set to ``TRUE`` if all
|
||||
the variables ``<required-var>...`` are valid and any optional
|
||||
constraints are satisfied, and ``FALSE`` otherwise. A success or
|
||||
failure message may be displayed based on the results and on
|
||||
whether the ``REQUIRED`` and/or ``QUIET`` option was given to
|
||||
the :command:`find_package` call.
|
||||
|
||||
The options are:
|
||||
|
||||
``(DEFAULT_MSG|<custom-failure-message>)``
|
||||
In the simple signature this specifies the failure message.
|
||||
Use ``DEFAULT_MSG`` to ask for a default message to be computed
|
||||
(recommended). Not valid in the full signature.
|
||||
|
||||
``FOUND_VAR <result-var>``
|
||||
Obsolete. Specifies either ``<PackageName>_FOUND`` or
|
||||
``<PACKAGENAME>_FOUND`` as the result variable. This exists only
|
||||
for compatibility with older versions of CMake and is now ignored.
|
||||
Result variables of both names are always set for compatibility.
|
||||
|
||||
``REQUIRED_VARS <required-var>...``
|
||||
Specify the variables which are required for this package.
|
||||
These may be named in the generated failure message asking the
|
||||
user to set the missing variable values. Therefore these should
|
||||
typically be cache entries such as ``FOO_LIBRARY`` and not output
|
||||
variables like ``FOO_LIBRARIES``.
|
||||
|
||||
``VERSION_VAR <version-var>``
|
||||
Specify the name of a variable that holds the version of the package
|
||||
that has been found. This version will be checked against the
|
||||
(potentially) specified required version given to the
|
||||
:command:`find_package` call, including its ``EXACT`` option.
|
||||
The default messages include information about the required
|
||||
version and the version which has been actually found, both
|
||||
if the version is ok or not.
|
||||
|
||||
``HANDLE_COMPONENTS``
|
||||
Enable handling of package components. In this case, the command
|
||||
will report which components have been found and which are missing,
|
||||
and the ``<PackageName>_FOUND`` variable will be set to ``FALSE``
|
||||
if any of the required components (i.e. not the ones listed after
|
||||
the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are
|
||||
missing.
|
||||
|
||||
``CONFIG_MODE``
|
||||
Specify that the calling find module is a wrapper around a
|
||||
call to ``find_package(<PackageName> NO_MODULE)``. This implies
|
||||
a ``VERSION_VAR`` value of ``<PackageName>_VERSION``. The command
|
||||
will automatically check whether the package configuration file
|
||||
was found.
|
||||
|
||||
``FAIL_MESSAGE <custom-failure-message>``
|
||||
Specify a custom failure message instead of using the default
|
||||
generated message. Not recommended.
|
||||
|
||||
Example for the simple signature:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package_handle_standard_args(LibXml2 DEFAULT_MSG
|
||||
LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR)
|
||||
|
||||
The ``LibXml2`` package is considered to be found if both
|
||||
``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid.
|
||||
Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found
|
||||
and ``REQUIRED`` was used, it fails with a
|
||||
:command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was
|
||||
used or not. If it is found, success will be reported, including
|
||||
the content of the first ``<required-var>``. On repeated CMake runs,
|
||||
the same message will not be printed again.
|
||||
|
||||
Example for the full signature:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package_handle_standard_args(LibArchive
|
||||
REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR
|
||||
VERSION_VAR LibArchive_VERSION)
|
||||
|
||||
In this case, the ``LibArchive`` package is considered to be found if
|
||||
both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid.
|
||||
Also the version of ``LibArchive`` will be checked by using the version
|
||||
contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given,
|
||||
the default messages will be printed.
|
||||
|
||||
Another example for the full signature:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4)
|
||||
find_package_handle_standard_args(Automoc4 CONFIG_MODE)
|
||||
|
||||
In this case, a ``FindAutmoc4.cmake`` module wraps a call to
|
||||
``find_package(Automoc4 NO_MODULE)`` and adds an additional search
|
||||
directory for ``automoc4``. Then the call to
|
||||
``find_package_handle_standard_args`` produces a proper success/failure
|
||||
message.
|
||||
#]=======================================================================]
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage_SLIC3R.cmake)
|
||||
|
||||
# internal helper macro
|
||||
macro(_FPHSA_FAILURE_MESSAGE _msg)
|
||||
if (${_NAME}_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "${_msg}")
|
||||
else ()
|
||||
if (NOT ${_NAME}_FIND_QUIETLY)
|
||||
message(STATUS "${_msg}")
|
||||
endif ()
|
||||
endif ()
|
||||
endmacro()
|
||||
|
||||
|
||||
# internal helper macro to generate the failure message when used in CONFIG_MODE:
|
||||
macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE)
|
||||
# <PackageName>_CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found:
|
||||
if(${_NAME}_CONFIG)
|
||||
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})")
|
||||
else()
|
||||
# If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version.
|
||||
# List them all in the error message:
|
||||
if(${_NAME}_CONSIDERED_CONFIGS)
|
||||
set(configsText "")
|
||||
list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount)
|
||||
math(EXPR configsCount "${configsCount} - 1")
|
||||
foreach(currentConfigIndex RANGE ${configsCount})
|
||||
list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename)
|
||||
list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version)
|
||||
string(APPEND configsText " ${filename} (version ${version})\n")
|
||||
endforeach()
|
||||
if (${_NAME}_NOT_FOUND_MESSAGE)
|
||||
string(APPEND configsText " Reason given by package: ${${_NAME}_NOT_FOUND_MESSAGE}\n")
|
||||
endif()
|
||||
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}")
|
||||
|
||||
else()
|
||||
# Simple case: No Config-file was found at all:
|
||||
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}")
|
||||
endif()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
|
||||
function(FIND_PACKAGE_HANDLE_STANDARD_ARGS_SLIC3R _NAME _FIRST_ARG)
|
||||
|
||||
# Set up the arguments for `cmake_parse_arguments`.
|
||||
set(options CONFIG_MODE HANDLE_COMPONENTS)
|
||||
set(oneValueArgs FAIL_MESSAGE VERSION_VAR FOUND_VAR)
|
||||
set(multiValueArgs REQUIRED_VARS)
|
||||
|
||||
# Check whether we are in 'simple' or 'extended' mode:
|
||||
set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} )
|
||||
list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX)
|
||||
|
||||
if(${INDEX} EQUAL -1)
|
||||
set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG})
|
||||
set(FPHSA_REQUIRED_VARS ${ARGN})
|
||||
set(FPHSA_VERSION_VAR)
|
||||
else()
|
||||
cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN})
|
||||
|
||||
if(FPHSA_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"")
|
||||
endif()
|
||||
|
||||
if(NOT FPHSA_FAIL_MESSAGE)
|
||||
set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG")
|
||||
endif()
|
||||
|
||||
# In config-mode, we rely on the variable <PackageName>_CONFIG, which is set by find_package()
|
||||
# when it successfully found the config-file, including version checking:
|
||||
if(FPHSA_CONFIG_MODE)
|
||||
list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG)
|
||||
list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS)
|
||||
set(FPHSA_VERSION_VAR ${_NAME}_VERSION)
|
||||
endif()
|
||||
|
||||
if(NOT FPHSA_REQUIRED_VARS)
|
||||
message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# now that we collected all arguments, process them
|
||||
|
||||
if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG")
|
||||
set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}")
|
||||
endif()
|
||||
|
||||
list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR)
|
||||
|
||||
string(TOUPPER ${_NAME} _NAME_UPPER)
|
||||
string(TOLOWER ${_NAME} _NAME_LOWER)
|
||||
|
||||
if(FPHSA_FOUND_VAR)
|
||||
if(FPHSA_FOUND_VAR MATCHES "^${_NAME}_FOUND$" OR FPHSA_FOUND_VAR MATCHES "^${_NAME_UPPER}_FOUND$")
|
||||
set(_FOUND_VAR ${FPHSA_FOUND_VAR})
|
||||
else()
|
||||
message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_NAME}_FOUND\" and \"${_NAME_UPPER}_FOUND\" are valid names.")
|
||||
endif()
|
||||
else()
|
||||
set(_FOUND_VAR ${_NAME_UPPER}_FOUND)
|
||||
endif()
|
||||
|
||||
# collect all variables which were not found, so they can be printed, so the
|
||||
# user knows better what went wrong (#6375)
|
||||
set(MISSING_VARS "")
|
||||
set(DETAILS "")
|
||||
# check if all passed variables are valid
|
||||
set(FPHSA_FOUND_${_NAME} TRUE)
|
||||
foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS})
|
||||
if(NOT ${_CURRENT_VAR})
|
||||
set(FPHSA_FOUND_${_NAME} FALSE)
|
||||
string(APPEND MISSING_VARS " ${_CURRENT_VAR}")
|
||||
else()
|
||||
string(APPEND DETAILS "[${${_CURRENT_VAR}}]")
|
||||
endif()
|
||||
endforeach()
|
||||
if(FPHSA_FOUND_${_NAME})
|
||||
set(${_NAME}_FOUND TRUE)
|
||||
set(${_NAME_UPPER}_FOUND TRUE)
|
||||
else()
|
||||
set(${_NAME}_FOUND FALSE)
|
||||
set(${_NAME_UPPER}_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
# component handling
|
||||
unset(FOUND_COMPONENTS_MSG)
|
||||
unset(MISSING_COMPONENTS_MSG)
|
||||
|
||||
if(FPHSA_HANDLE_COMPONENTS)
|
||||
foreach(comp ${${_NAME}_FIND_COMPONENTS})
|
||||
if(${_NAME}_${comp}_FOUND)
|
||||
|
||||
if(NOT DEFINED FOUND_COMPONENTS_MSG)
|
||||
set(FOUND_COMPONENTS_MSG "found components: ")
|
||||
endif()
|
||||
string(APPEND FOUND_COMPONENTS_MSG " ${comp}")
|
||||
|
||||
else()
|
||||
|
||||
if(NOT DEFINED MISSING_COMPONENTS_MSG)
|
||||
set(MISSING_COMPONENTS_MSG "missing components: ")
|
||||
endif()
|
||||
string(APPEND MISSING_COMPONENTS_MSG " ${comp}")
|
||||
|
||||
if(${_NAME}_FIND_REQUIRED_${comp})
|
||||
set(${_NAME}_FOUND FALSE)
|
||||
string(APPEND MISSING_VARS " ${comp}")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
endforeach()
|
||||
set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}")
|
||||
string(APPEND DETAILS "[c${COMPONENT_MSG}]")
|
||||
endif()
|
||||
|
||||
# version handling:
|
||||
set(VERSION_MSG "")
|
||||
set(VERSION_OK TRUE)
|
||||
|
||||
# check with DEFINED here as the requested or found version may be "0"
|
||||
if (DEFINED ${_NAME}_FIND_VERSION)
|
||||
if(DEFINED ${FPHSA_VERSION_VAR})
|
||||
set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}})
|
||||
|
||||
if(${_NAME}_FIND_VERSION_EXACT) # exact version required
|
||||
# count the dots in the version string
|
||||
string(REGEX REPLACE "[^.]" "" _VERSION_DOTS "${_FOUND_VERSION}")
|
||||
# add one dot because there is one dot more than there are components
|
||||
string(LENGTH "${_VERSION_DOTS}." _VERSION_DOTS)
|
||||
if (_VERSION_DOTS GREATER ${_NAME}_FIND_VERSION_COUNT)
|
||||
# Because of the C++ implementation of find_package() ${_NAME}_FIND_VERSION_COUNT
|
||||
# is at most 4 here. Therefore a simple lookup table is used.
|
||||
if (${_NAME}_FIND_VERSION_COUNT EQUAL 1)
|
||||
set(_VERSION_REGEX "[^.]*")
|
||||
elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 2)
|
||||
set(_VERSION_REGEX "[^.]*\\.[^.]*")
|
||||
elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 3)
|
||||
set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*")
|
||||
else ()
|
||||
set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*")
|
||||
endif ()
|
||||
string(REGEX REPLACE "^(${_VERSION_REGEX})\\..*" "\\1" _VERSION_HEAD "${_FOUND_VERSION}")
|
||||
unset(_VERSION_REGEX)
|
||||
if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _VERSION_HEAD)
|
||||
set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"")
|
||||
set(VERSION_OK FALSE)
|
||||
else ()
|
||||
set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")")
|
||||
endif ()
|
||||
unset(_VERSION_HEAD)
|
||||
else ()
|
||||
if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _FOUND_VERSION)
|
||||
set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"")
|
||||
set(VERSION_OK FALSE)
|
||||
else ()
|
||||
set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")")
|
||||
endif ()
|
||||
endif ()
|
||||
unset(_VERSION_DOTS)
|
||||
|
||||
else() # minimum version specified:
|
||||
if (${_NAME}_FIND_VERSION VERSION_GREATER _FOUND_VERSION)
|
||||
set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"")
|
||||
set(VERSION_OK FALSE)
|
||||
else ()
|
||||
set(VERSION_MSG "(found suitable version \"${_FOUND_VERSION}\", minimum required is \"${${_NAME}_FIND_VERSION}\")")
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
else()
|
||||
|
||||
# if the package was not found, but a version was given, add that to the output:
|
||||
if(${_NAME}_FIND_VERSION_EXACT)
|
||||
set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")")
|
||||
else()
|
||||
set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
else ()
|
||||
# Check with DEFINED as the found version may be 0.
|
||||
if(DEFINED ${FPHSA_VERSION_VAR})
|
||||
set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if(VERSION_OK)
|
||||
string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]")
|
||||
else()
|
||||
set(${_NAME}_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
|
||||
# print the result:
|
||||
if (${_NAME}_FOUND)
|
||||
FIND_PACKAGE_MESSAGE_SLIC3R(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}")
|
||||
else ()
|
||||
|
||||
if(FPHSA_CONFIG_MODE)
|
||||
_FPHSA_HANDLE_FAILURE_CONFIG_MODE()
|
||||
else()
|
||||
if(NOT VERSION_OK)
|
||||
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})")
|
||||
else()
|
||||
_FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endif ()
|
||||
|
||||
set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE)
|
||||
set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE)
|
||||
endfunction()
|
54
cmake/modules/FindPackageMessage_SLIC3R.cmake
Normal file
|
@ -0,0 +1,54 @@
|
|||
# Modified from the CMake github master.
|
||||
# required by the bundled FindCURL.cmake
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#.rst:
|
||||
# FindPackageMessage
|
||||
# ------------------
|
||||
#
|
||||
#
|
||||
#
|
||||
# FIND_PACKAGE_MESSAGE(<name> "message for user" "find result details")
|
||||
#
|
||||
# This macro is intended to be used in FindXXX.cmake modules files. It
|
||||
# will print a message once for each unique find result. This is useful
|
||||
# for telling the user where a package was found. The first argument
|
||||
# specifies the name (XXX) of the package. The second argument
|
||||
# specifies the message to display. The third argument lists details
|
||||
# about the find result so that if they change the message will be
|
||||
# displayed again. The macro also obeys the QUIET argument to the
|
||||
# find_package command.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# if(X11_FOUND)
|
||||
# FIND_PACKAGE_MESSAGE(X11 "Found X11: ${X11_X11_LIB}"
|
||||
# "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]")
|
||||
# else()
|
||||
# ...
|
||||
# endif()
|
||||
|
||||
function(FIND_PACKAGE_MESSAGE_SLIC3R pkg msg details)
|
||||
# Avoid printing a message repeatedly for the same find result.
|
||||
if(NOT ${pkg}_FIND_QUIETLY)
|
||||
string(REPLACE "\n" "" details "${details}")
|
||||
set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg})
|
||||
if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}")
|
||||
# The message has not yet been printed.
|
||||
message(STATUS "${msg}")
|
||||
|
||||
# Save the find details in the cache to avoid printing the same
|
||||
# message again.
|
||||
set("${DETAILS_VAR}" "${details}"
|
||||
CACHE INTERNAL "Details about finding ${pkg}")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
|
@ -280,7 +280,7 @@ if(NOT TBB_FOUND)
|
|||
##################################
|
||||
|
||||
if(NOT CMAKE_VERSION VERSION_LESS 3.0 AND TBB_FOUND)
|
||||
add_library(tbb SHARED IMPORTED)
|
||||
add_library(tbb UNKNOWN IMPORTED)
|
||||
set_target_properties(tbb PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${TBB_INCLUDE_DIRS}
|
||||
IMPORTED_LOCATION ${TBB_LIBRARIES})
|
||||
|
@ -288,7 +288,7 @@ if(NOT TBB_FOUND)
|
|||
set_target_properties(tbb PROPERTIES
|
||||
INTERFACE_COMPILE_DEFINITIONS "$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:TBB_USE_DEBUG=1>"
|
||||
IMPORTED_LOCATION_DEBUG ${TBB_LIBRARIES_DEBUG}
|
||||
IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_DEBUG}
|
||||
IMPORTED_LOCATION_RELWITHDEBINFO ${TBB_LIBRARIES_RELEASE}
|
||||
IMPORTED_LOCATION_RELEASE ${TBB_LIBRARIES_RELEASE}
|
||||
IMPORTED_LOCATION_MINSIZEREL ${TBB_LIBRARIES_RELEASE}
|
||||
)
|
||||
|
@ -310,6 +310,7 @@ if(NOT TBB_FOUND)
|
|||
unset(TBB_DEFAULT_SEARCH_DIR)
|
||||
|
||||
if(TBB_DEBUG)
|
||||
message(STATUS " TBB_FOUND = ${TBB_FOUND}")
|
||||
message(STATUS " TBB_INCLUDE_DIRS = ${TBB_INCLUDE_DIRS}")
|
||||
message(STATUS " TBB_DEFINITIONS = ${TBB_DEFINITIONS}")
|
||||
message(STATUS " TBB_LIBRARIES = ${TBB_LIBRARIES}")
|
||||
|
|
214
cmake/modules/PrecompiledHeader.cmake
Normal file
|
@ -0,0 +1,214 @@
|
|||
# Function for setting up precompiled headers. Usage:
|
||||
#
|
||||
# add_library/executable(target
|
||||
# pchheader.c pchheader.cpp pchheader.h)
|
||||
#
|
||||
# add_precompiled_header(target pchheader.h
|
||||
# [FORCEINCLUDE]
|
||||
# [SOURCE_C pchheader.c]
|
||||
# [SOURCE_CXX pchheader.cpp])
|
||||
#
|
||||
# Options:
|
||||
#
|
||||
# FORCEINCLUDE: Add compiler flags to automatically include the
|
||||
# pchheader.h from every source file. Works with both GCC and
|
||||
# MSVC. This is recommended.
|
||||
#
|
||||
# SOURCE_C/CXX: Specifies the .c/.cpp source file that includes
|
||||
# pchheader.h for generating the pre-compiled header
|
||||
# output. Defaults to pchheader.c. Only required for MSVC.
|
||||
#
|
||||
# Caveats:
|
||||
#
|
||||
# * Its not currently possible to use the same precompiled-header in
|
||||
# more than a single target in the same directory (No way to set
|
||||
# the source file properties differently for each target).
|
||||
#
|
||||
# * MSVC: A source file with the same name as the header must exist
|
||||
# and be included in the target (E.g. header.cpp). Name of file
|
||||
# can be changed using the SOURCE_CXX/SOURCE_C options.
|
||||
#
|
||||
# License:
|
||||
#
|
||||
# Copyright (C) 2009-2017 Lars Christensen <larsch@belunktum.dk>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person
|
||||
# obtaining a copy of this software and associated documentation files
|
||||
# (the 'Software') deal in the Software without restriction,
|
||||
# including without limitation the rights to use, copy, modify, merge,
|
||||
# publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
# and to permit persons to whom the Software is furnished to do so,
|
||||
# subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be
|
||||
# included in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
macro(combine_arguments _variable)
|
||||
set(_result "")
|
||||
foreach(_element ${${_variable}})
|
||||
set(_result "${_result} \"${_element}\"")
|
||||
endforeach()
|
||||
string(STRIP "${_result}" _result)
|
||||
set(${_variable} "${_result}")
|
||||
endmacro()
|
||||
|
||||
function(export_all_flags _filename)
|
||||
set(_include_directories "$<TARGET_PROPERTY:${_target},INCLUDE_DIRECTORIES>")
|
||||
set(_compile_definitions "$<TARGET_PROPERTY:${_target},COMPILE_DEFINITIONS>")
|
||||
set(_compile_flags "$<TARGET_PROPERTY:${_target},COMPILE_FLAGS>")
|
||||
set(_compile_options "$<TARGET_PROPERTY:${_target},COMPILE_OPTIONS>")
|
||||
set(_include_directories "$<$<BOOL:${_include_directories}>:-I$<JOIN:${_include_directories},\n-I>\n>")
|
||||
set(_compile_definitions "$<$<BOOL:${_compile_definitions}>:-D$<JOIN:${_compile_definitions},\n-D>\n>")
|
||||
set(_compile_flags "$<$<BOOL:${_compile_flags}>:$<JOIN:${_compile_flags},\n>\n>")
|
||||
set(_compile_options "$<$<BOOL:${_compile_options}>:$<JOIN:${_compile_options},\n>\n>")
|
||||
file(GENERATE OUTPUT "${_filename}" CONTENT "${_compile_definitions}${_include_directories}${_compile_flags}${_compile_options}\n")
|
||||
endfunction()
|
||||
|
||||
function(add_precompiled_header _target _input)
|
||||
cmake_parse_arguments(_PCH "FORCEINCLUDE" "SOURCE_CXX;SOURCE_C" "" ${ARGN})
|
||||
|
||||
get_filename_component(_input_we ${_input} NAME_WE)
|
||||
if(NOT _PCH_SOURCE_CXX)
|
||||
set(_PCH_SOURCE_CXX "${_input_we}.cpp")
|
||||
endif()
|
||||
if(NOT _PCH_SOURCE_C)
|
||||
set(_PCH_SOURCE_C "${_input_we}.c")
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
set(_pch_cxx_pch "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/cxx_${_input_we}.pch")
|
||||
set(_pch_c_pch "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/c_${_input_we}.pch")
|
||||
|
||||
get_target_property(sources ${_target} SOURCES)
|
||||
foreach(_source ${sources})
|
||||
set(_pch_compile_flags "")
|
||||
if(_source MATCHES \\.\(cc|cxx|cpp|c\)$)
|
||||
if(_source MATCHES \\.\(cpp|cxx|cc\)$)
|
||||
set(_pch_header "${_input}")
|
||||
set(_pch "${_pch_cxx_pch}")
|
||||
else()
|
||||
set(_pch_header "${_input}")
|
||||
set(_pch "${_pch_c_pch}")
|
||||
endif()
|
||||
|
||||
if(_source STREQUAL "${_PCH_SOURCE_CXX}")
|
||||
set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_cxx_pch}\" \"/Yc${_input}\"")
|
||||
set(_pch_source_cxx_found TRUE)
|
||||
set_source_files_properties("${_source}" PROPERTIES OBJECT_OUTPUTS "${_pch_cxx_pch}")
|
||||
elseif(_source STREQUAL "${_PCH_SOURCE_C}")
|
||||
set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_c_pch}\" \"/Yc${_input}\"")
|
||||
set(_pch_source_c_found TRUE)
|
||||
set_source_files_properties("${_source}" PROPERTIES OBJECT_OUTPUTS "${_pch_c_pch}")
|
||||
else()
|
||||
if(_source MATCHES \\.\(cpp|cxx|cc\)$)
|
||||
set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_cxx_pch}\" \"/Yu${_input}\"")
|
||||
set(_pch_source_cxx_needed TRUE)
|
||||
set_source_files_properties("${_source}" PROPERTIES OBJECT_DEPENDS "${_pch_cxx_pch}")
|
||||
else()
|
||||
set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_c_pch}\" \"/Yu${_input}\"")
|
||||
set(_pch_source_c_needed TRUE)
|
||||
set_source_files_properties("${_source}" PROPERTIES OBJECT_DEPENDS "${_pch_c_pch}")
|
||||
endif()
|
||||
if(_PCH_FORCEINCLUDE)
|
||||
set(_pch_compile_flags "${_pch_compile_flags} /FI${_input}")
|
||||
endif(_PCH_FORCEINCLUDE)
|
||||
endif()
|
||||
|
||||
get_source_file_property(_object_depends "${_source}" OBJECT_DEPENDS)
|
||||
if(NOT _object_depends)
|
||||
set(_object_depends)
|
||||
endif()
|
||||
if(_PCH_FORCEINCLUDE)
|
||||
list(APPEND _object_depends "${CMAKE_CURRENT_SOURCE_DIR}/${_pch_header}")
|
||||
endif()
|
||||
|
||||
set_source_files_properties(${_source} PROPERTIES
|
||||
COMPILE_FLAGS "${_pch_compile_flags}"
|
||||
OBJECT_DEPENDS "${_object_depends}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(_pch_source_cxx_needed AND NOT _pch_source_cxx_found)
|
||||
message(FATAL_ERROR "A source file ${_PCH_SOURCE_CXX} for ${_input} is required for MSVC builds. Can be set with the SOURCE_CXX option.")
|
||||
endif()
|
||||
if(_pch_source_c_needed AND NOT _pch_source_c_found)
|
||||
message(FATAL_ERROR "A source file ${_PCH_SOURCE_C} for ${_input} is required for MSVC builds. Can be set with the SOURCE_C option.")
|
||||
endif()
|
||||
endif(MSVC)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
get_filename_component(_name ${_input} NAME)
|
||||
set(_pch_header "${CMAKE_CURRENT_SOURCE_DIR}/${_input}")
|
||||
set(_pch_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/${_target}_pch")
|
||||
set(_pchfile "${_pch_binary_dir}/${_input}")
|
||||
set(_outdir "${CMAKE_CURRENT_BINARY_DIR}/${_target}_pch/${_name}.gch")
|
||||
file(MAKE_DIRECTORY "${_outdir}")
|
||||
set(_output_cxx "${_outdir}/.c++")
|
||||
set(_output_c "${_outdir}/.c")
|
||||
|
||||
set(_pch_flags_file "${_pch_binary_dir}/compile_flags.rsp")
|
||||
export_all_flags("${_pch_flags_file}")
|
||||
set(_compiler_FLAGS "@${_pch_flags_file}")
|
||||
add_custom_command(
|
||||
OUTPUT "${_pchfile}"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy "${_pch_header}" "${_pchfile}"
|
||||
DEPENDS "${_pch_header}"
|
||||
COMMENT "Updating ${_name}")
|
||||
add_custom_command(
|
||||
OUTPUT "${_output_cxx}"
|
||||
COMMAND "${CMAKE_CXX_COMPILER}" ${_compiler_FLAGS} -x c++-header -o "${_output_cxx}" "${_pchfile}"
|
||||
DEPENDS "${_pchfile}" "${_pch_flags_file}"
|
||||
COMMENT "Precompiling ${_name} for ${_target} (C++)")
|
||||
add_custom_command(
|
||||
OUTPUT "${_output_c}"
|
||||
COMMAND "${CMAKE_C_COMPILER}" ${_compiler_FLAGS} -x c-header -o "${_output_c}" "${_pchfile}"
|
||||
DEPENDS "${_pchfile}" "${_pch_flags_file}"
|
||||
COMMENT "Precompiling ${_name} for ${_target} (C)")
|
||||
|
||||
get_property(_sources TARGET ${_target} PROPERTY SOURCES)
|
||||
foreach(_source ${_sources})
|
||||
set(_pch_compile_flags "")
|
||||
|
||||
if(_source MATCHES \\.\(cc|cxx|cpp|c\)$)
|
||||
get_source_file_property(_pch_compile_flags "${_source}" COMPILE_FLAGS)
|
||||
if(NOT _pch_compile_flags)
|
||||
set(_pch_compile_flags)
|
||||
endif()
|
||||
separate_arguments(_pch_compile_flags)
|
||||
list(APPEND _pch_compile_flags -Winvalid-pch)
|
||||
if(_PCH_FORCEINCLUDE)
|
||||
list(APPEND _pch_compile_flags -include "${_pchfile}")
|
||||
else(_PCH_FORCEINCLUDE)
|
||||
list(APPEND _pch_compile_flags "-I${_pch_binary_dir}")
|
||||
endif(_PCH_FORCEINCLUDE)
|
||||
|
||||
get_source_file_property(_object_depends "${_source}" OBJECT_DEPENDS)
|
||||
if(NOT _object_depends)
|
||||
set(_object_depends)
|
||||
endif()
|
||||
list(APPEND _object_depends "${_pchfile}")
|
||||
if(_source MATCHES \\.\(cc|cxx|cpp\)$)
|
||||
list(APPEND _object_depends "${_output_cxx}")
|
||||
else()
|
||||
list(APPEND _object_depends "${_output_c}")
|
||||
endif()
|
||||
|
||||
combine_arguments(_pch_compile_flags)
|
||||
set_source_files_properties(${_source} PROPERTIES
|
||||
COMPILE_FLAGS "${_pch_compile_flags}"
|
||||
OBJECT_DEPENDS "${_object_depends}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
endfunction()
|
77
cmake/modules/SelectLibraryConfigurations_SLIC3R.cmake
Normal file
|
@ -0,0 +1,77 @@
|
|||
# Modified from the CMake github master.
|
||||
# required by the bundled FindCURL.cmake
|
||||
|
||||
|
||||
|
||||
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||
|
||||
#.rst:
|
||||
# SelectLibraryConfigurations
|
||||
# ---------------------------
|
||||
#
|
||||
#
|
||||
#
|
||||
# select_library_configurations( basename )
|
||||
#
|
||||
# This macro takes a library base name as an argument, and will choose
|
||||
# good values for basename_LIBRARY, basename_LIBRARIES,
|
||||
# basename_LIBRARY_DEBUG, and basename_LIBRARY_RELEASE depending on what
|
||||
# has been found and set. If only basename_LIBRARY_RELEASE is defined,
|
||||
# basename_LIBRARY will be set to the release value, and
|
||||
# basename_LIBRARY_DEBUG will be set to basename_LIBRARY_DEBUG-NOTFOUND.
|
||||
# If only basename_LIBRARY_DEBUG is defined, then basename_LIBRARY will
|
||||
# take the debug value, and basename_LIBRARY_RELEASE will be set to
|
||||
# basename_LIBRARY_RELEASE-NOTFOUND.
|
||||
#
|
||||
# If the generator supports configuration types, then basename_LIBRARY
|
||||
# and basename_LIBRARIES will be set with debug and optimized flags
|
||||
# specifying the library to be used for the given configuration. If no
|
||||
# build type has been set or the generator in use does not support
|
||||
# configuration types, then basename_LIBRARY and basename_LIBRARIES will
|
||||
# take only the release value, or the debug value if the release one is
|
||||
# not set.
|
||||
|
||||
# This macro was adapted from the FindQt4 CMake module and is maintained by Will
|
||||
# Dicharry <wdicharry@stellarscience.com>.
|
||||
|
||||
macro( select_library_configurations_SLIC3R basename )
|
||||
if(NOT ${basename}_LIBRARY_RELEASE)
|
||||
set(${basename}_LIBRARY_RELEASE "${basename}_LIBRARY_RELEASE-NOTFOUND" CACHE FILEPATH "Path to a library.")
|
||||
endif()
|
||||
if(NOT ${basename}_LIBRARY_DEBUG)
|
||||
set(${basename}_LIBRARY_DEBUG "${basename}_LIBRARY_DEBUG-NOTFOUND" CACHE FILEPATH "Path to a library.")
|
||||
endif()
|
||||
|
||||
get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if( ${basename}_LIBRARY_DEBUG AND ${basename}_LIBRARY_RELEASE AND
|
||||
NOT ${basename}_LIBRARY_DEBUG STREQUAL ${basename}_LIBRARY_RELEASE AND
|
||||
( _isMultiConfig OR CMAKE_BUILD_TYPE ) )
|
||||
# if the generator is multi-config or if CMAKE_BUILD_TYPE is set for
|
||||
# single-config generators, set optimized and debug libraries
|
||||
set( ${basename}_LIBRARY "" )
|
||||
foreach( _libname IN LISTS ${basename}_LIBRARY_RELEASE )
|
||||
list( APPEND ${basename}_LIBRARY optimized "${_libname}" )
|
||||
endforeach()
|
||||
foreach( _libname IN LISTS ${basename}_LIBRARY_DEBUG )
|
||||
list( APPEND ${basename}_LIBRARY debug "${_libname}" )
|
||||
endforeach()
|
||||
elseif( ${basename}_LIBRARY_RELEASE )
|
||||
set( ${basename}_LIBRARY ${${basename}_LIBRARY_RELEASE} )
|
||||
elseif( ${basename}_LIBRARY_DEBUG )
|
||||
set( ${basename}_LIBRARY ${${basename}_LIBRARY_DEBUG} )
|
||||
else()
|
||||
set( ${basename}_LIBRARY "${basename}_LIBRARY-NOTFOUND")
|
||||
endif()
|
||||
|
||||
set( ${basename}_LIBRARIES "${${basename}_LIBRARY}" )
|
||||
|
||||
if( ${basename}_LIBRARY )
|
||||
set( ${basename}_FOUND TRUE )
|
||||
endif()
|
||||
|
||||
mark_as_advanced( ${basename}_LIBRARY_RELEASE
|
||||
${basename}_LIBRARY_DEBUG
|
||||
)
|
||||
endmacro()
|
62
deps/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
#
|
||||
# This CMake project downloads, configures and builds Slic3r PE dependencies on Unix and Windows.
|
||||
#
|
||||
# When using this script, it's recommended to perform an out-of-source build using CMake.
|
||||
#
|
||||
# All the dependencies are installed in a `destdir` directory in the root of the build directory,
|
||||
# in a traditional Unix-style prefix structure. The destdir can be used directly by CMake
|
||||
# when building Slic3r - to do this, set the CMAKE_PREFIX_PATH to ${destdir}/usr/local.
|
||||
#
|
||||
# For better clarity of console output, it's recommended to _not_ use a parallelized build
|
||||
# for the top-level command, ie. use `make -j 1` or `ninja -j 1` to force single-threaded top-level
|
||||
# build. This doesn't degrade performance as individual dependencies are built in parallel fashion
|
||||
# if supported by the dependency.
|
||||
#
|
||||
# On Windows, architecture (64 vs 32 bits) is judged based on the compiler variant.
|
||||
# To build dependencies for either 64 or 32 bit OS, use the respective compiler command line.
|
||||
#
|
||||
|
||||
project(Slic3r-deps)
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
include(ExternalProject)
|
||||
|
||||
include(ProcessorCount)
|
||||
ProcessorCount(NPROC)
|
||||
if (NPROC EQUAL 0)
|
||||
set(NPROC 1)
|
||||
endif ()
|
||||
|
||||
set(DESTDIR "${CMAKE_CURRENT_BINARY_DIR}/destdir" CACHE PATH "Destination directory")
|
||||
option(DEP_DEBUG "Build debug variants (currently only works on Windows)" ON)
|
||||
|
||||
|
||||
if (MSVC)
|
||||
if ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
|
||||
message(STATUS "\nDetected 64-bit compiler => building 64-bit deps bundle\n")
|
||||
set(DEPS_BITS 64)
|
||||
include("deps-windows.cmake")
|
||||
elseif ("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
|
||||
message(STATUS "\nDetected 32-bit compiler => building 32-bit deps bundle\n")
|
||||
set(DEPS_BITS 32)
|
||||
include("deps-windows.cmake")
|
||||
else ()
|
||||
message(FATAL_ERROR "Unable to detect architecture")
|
||||
endif ()
|
||||
else ()
|
||||
include("deps-unix-static.cmake")
|
||||
endif()
|
||||
|
||||
add_custom_target(deps ALL
|
||||
DEPENDS
|
||||
dep_boost
|
||||
dep_tbb
|
||||
dep_libcurl
|
||||
dep_wxwidgets
|
||||
dep_gtest
|
||||
dep_nlopt
|
||||
dep_libpng
|
||||
)
|
||||
|
||||
# Note: I'm not using any of the LOG_xxx options in ExternalProject_Add() commands
|
||||
# because they seem to generate bogus build files (possibly a bug in ExternalProject).
|
167
deps/deps-unix-static.cmake
vendored
Normal file
|
@ -0,0 +1,167 @@
|
|||
|
||||
ExternalProject_Add(dep_boost
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://dl.bintray.com/boostorg/release/1.66.0/source/boost_1_66_0.tar.gz"
|
||||
URL_HASH SHA256=bd0df411efd9a585e5a2212275f8762079fed8842264954675a4fddc46cfcf60
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND ./bootstrap.sh
|
||||
--with-libraries=system,filesystem,thread,log,locale,regex
|
||||
"--prefix=${DESTDIR}/usr/local"
|
||||
BUILD_COMMAND ./b2
|
||||
-j ${NPROC}
|
||||
link=static
|
||||
variant=release
|
||||
threading=multi
|
||||
boost.locale.icu=off
|
||||
cxxflags=-fPIC cflags=-fPIC
|
||||
install
|
||||
INSTALL_COMMAND "" # b2 does that already
|
||||
)
|
||||
|
||||
ExternalProject_Add(dep_tbb
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz"
|
||||
URL_HASH SHA256=0545cb6033bd1873fcae3ea304def720a380a88292726943ae3b9b207f322efe
|
||||
CMAKE_ARGS -DTBB_BUILD_SHARED=OFF
|
||||
-DTBB_BUILD_TESTS=OFF
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
|
||||
)
|
||||
|
||||
ExternalProject_Add(dep_gtest
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://github.com/google/googletest/archive/release-1.8.1.tar.gz"
|
||||
URL_HASH SHA256=9bf1fe5182a604b4135edc1a425ae356c9ad15e9b23f9f12a02e80184c3a249c
|
||||
CMAKE_ARGS -DBUILD_GMOCK=OFF
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
|
||||
)
|
||||
|
||||
ExternalProject_Add(dep_nlopt
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz"
|
||||
URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_ARGS
|
||||
-DNLOPT_PYTHON=OFF
|
||||
-DNLOPT_OCTAVE=OFF
|
||||
-DNLOPT_MATLAB=OFF
|
||||
-DNLOPT_GUILE=OFF
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
|
||||
ExternalProject_Add(dep_zlib
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://zlib.net/zlib-1.2.11.tar.xz"
|
||||
URL_HASH SHA256=4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_ARGS
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
|
||||
ExternalProject_Add(dep_libpng
|
||||
DEPENDS dep_zlib
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "http://prdownloads.sourceforge.net/libpng/libpng-1.6.35.tar.xz?download"
|
||||
URL_HASH SHA256=23912ec8c9584917ed9b09c5023465d71709dce089be503c7867fec68a93bcd7
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_ARGS
|
||||
-DPNG_SHARED=OFF
|
||||
-DPNG_TESTS=OFF
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
|
||||
ExternalProject_Add(dep_libopenssl
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://github.com/openssl/openssl/archive/OpenSSL_1_1_0g.tar.gz"
|
||||
URL_HASH SHA256=8e9516b8635bb9113c51a7b5b27f9027692a56b104e75b709e588c3ffd6a0422
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND ./config
|
||||
"--prefix=${DESTDIR}/usr/local"
|
||||
no-shared
|
||||
no-ssl3-method
|
||||
no-dynamic-engine
|
||||
-Wa,--noexecstack
|
||||
BUILD_COMMAND make depend && make "-j${NPROC}"
|
||||
INSTALL_COMMAND make install_sw
|
||||
)
|
||||
|
||||
ExternalProject_Add(dep_libcurl
|
||||
EXCLUDE_FROM_ALL 1
|
||||
DEPENDS dep_libopenssl
|
||||
URL "https://curl.haxx.se/download/curl-7.58.0.tar.gz"
|
||||
URL_HASH SHA256=cc245bf9a1a42a45df491501d97d5593392a03f7b4f07b952793518d97666115
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND ./configure
|
||||
--enable-static
|
||||
--disable-shared
|
||||
"--with-ssl=${DESTDIR}/usr/local"
|
||||
--with-pic
|
||||
--enable-ipv6
|
||||
--enable-versioned-symbols
|
||||
--enable-threaded-resolver
|
||||
--with-random=/dev/urandom
|
||||
--with-ca-bundle=/etc/ssl/certs/ca-certificates.crt
|
||||
--disable-ldap
|
||||
--disable-ldaps
|
||||
--disable-manual
|
||||
--disable-rtsp
|
||||
--disable-dict
|
||||
--disable-telnet
|
||||
--disable-pop3
|
||||
--disable-imap
|
||||
--disable-smb
|
||||
--disable-smtp
|
||||
--disable-gopher
|
||||
--disable-crypto-auth
|
||||
--without-gssapi
|
||||
--without-libpsl
|
||||
--without-libidn2
|
||||
--without-gnutls
|
||||
--without-polarssl
|
||||
--without-mbedtls
|
||||
--without-cyassl
|
||||
--without-nss
|
||||
--without-axtls
|
||||
--without-brotli
|
||||
--without-libmetalink
|
||||
--without-libssh
|
||||
--without-libssh2
|
||||
--without-librtmp
|
||||
--without-nghttp2
|
||||
--without-zsh-functions-dir
|
||||
BUILD_COMMAND make "-j${NPROC}"
|
||||
INSTALL_COMMAND make install "DESTDIR=${DESTDIR}"
|
||||
)
|
||||
|
||||
ExternalProject_Add(dep_wxwidgets
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.1/wxWidgets-3.1.1.tar.bz2"
|
||||
URL_HASH SHA256=c925dfe17e8f8b09eb7ea9bfdcfcc13696a3e14e92750effd839f5e10726159e
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND ./configure
|
||||
"--prefix=${DESTDIR}/usr/local"
|
||||
--disable-shared
|
||||
--with-gtk=2
|
||||
--with-opengl
|
||||
--enable-unicode
|
||||
--enable-graphics_ctx
|
||||
--with-regex=builtin
|
||||
--with-libpng=builtin
|
||||
--with-libxpm=builtin
|
||||
--with-libjpeg=builtin
|
||||
--with-libtiff=builtin
|
||||
--with-zlib=builtin
|
||||
--with-expat=builtin
|
||||
--disable-precomp-headers
|
||||
--enable-debug_info
|
||||
--enable-debug_gdb
|
||||
BUILD_COMMAND make "-j${NPROC}" && make -C locale allmo
|
||||
INSTALL_COMMAND make install
|
||||
)
|
224
deps/deps-windows.cmake
vendored
Normal file
|
@ -0,0 +1,224 @@
|
|||
|
||||
if (${DEPS_BITS} EQUAL 32)
|
||||
set(DEP_MSVC_GEN "Visual Studio 12")
|
||||
else ()
|
||||
set(DEP_MSVC_GEN "Visual Studio 12 Win64")
|
||||
endif ()
|
||||
|
||||
|
||||
|
||||
if (${DEP_DEBUG})
|
||||
set(DEP_BOOST_DEBUG "debug")
|
||||
else ()
|
||||
set(DEP_BOOST_DEBUG "")
|
||||
endif ()
|
||||
|
||||
ExternalProject_Add(dep_boost
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://dl.bintray.com/boostorg/release/1.63.0/source/boost_1_63_0.tar.gz"
|
||||
URL_HASH SHA256=fe34a4e119798e10b8cc9e565b3b0284e9fd3977ec8a1b19586ad1dec397088b
|
||||
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=msvc-12.0
|
||||
link=static
|
||||
variant=release
|
||||
threading=multi
|
||||
boost.locale.icu=off
|
||||
"${DEP_BOOST_DEBUG}" release install
|
||||
INSTALL_COMMAND "" # b2 does that already
|
||||
)
|
||||
|
||||
|
||||
ExternalProject_Add(dep_tbb
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://github.com/wjakob/tbb/archive/a0dc9bf76d0120f917b641ed095360448cabc85b.tar.gz"
|
||||
URL_HASH SHA256=0545cb6033bd1873fcae3ea304def720a380a88292726943ae3b9b207f322efe
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_ARGS
|
||||
-DCMAKE_DEBUG_POSTFIX=d
|
||||
-DTBB_BUILD_SHARED=OFF
|
||||
-DTBB_BUILD_TESTS=OFF
|
||||
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
|
||||
BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
if (${DEP_DEBUG})
|
||||
ExternalProject_Get_Property(dep_tbb BINARY_DIR)
|
||||
ExternalProject_Add_Step(dep_tbb build_debug
|
||||
DEPENDEES build
|
||||
DEPENDERS install
|
||||
COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
|
||||
WORKING_DIRECTORY "${BINARY_DIR}"
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
ExternalProject_Add(dep_gtest
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://github.com/google/googletest/archive/release-1.8.1.tar.gz"
|
||||
URL_HASH SHA256=9bf1fe5182a604b4135edc1a425ae356c9ad15e9b23f9f12a02e80184c3a249c
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_ARGS -DBUILD_GMOCK=OFF
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
|
||||
BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
if (${DEP_DEBUG})
|
||||
ExternalProject_Get_Property(dep_gtest BINARY_DIR)
|
||||
ExternalProject_Add_Step(dep_gtest build_debug
|
||||
DEPENDEES build
|
||||
DEPENDERS install
|
||||
COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
|
||||
WORKING_DIRECTORY "${BINARY_DIR}"
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
ExternalProject_Add(dep_nlopt
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz"
|
||||
URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_ARGS
|
||||
-DNLOPT_PYTHON=OFF
|
||||
-DNLOPT_OCTAVE=OFF
|
||||
-DNLOPT_MATLAB=OFF
|
||||
-DNLOPT_GUILE=OFF
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
-DCMAKE_DEBUG_POSTFIX=d
|
||||
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
|
||||
BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
if (${DEP_DEBUG})
|
||||
ExternalProject_Get_Property(dep_nlopt BINARY_DIR)
|
||||
ExternalProject_Add_Step(dep_nlopt build_debug
|
||||
DEPENDEES build
|
||||
DEPENDERS install
|
||||
COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
|
||||
WORKING_DIRECTORY "${BINARY_DIR}"
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
ExternalProject_Add(dep_zlib
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://zlib.net/zlib-1.2.11.tar.xz"
|
||||
URL_HASH SHA256=4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_ARGS
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
|
||||
BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
if (${DEP_DEBUG})
|
||||
ExternalProject_Get_Property(dep_zlib BINARY_DIR)
|
||||
ExternalProject_Add_Step(dep_zlib build_debug
|
||||
DEPENDEES build
|
||||
DEPENDERS install
|
||||
COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
|
||||
WORKING_DIRECTORY "${BINARY_DIR}"
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
ExternalProject_Add(dep_libpng
|
||||
DEPENDS dep_zlib
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "http://prdownloads.sourceforge.net/libpng/libpng-1.6.35.tar.xz?download"
|
||||
URL_HASH SHA256=23912ec8c9584917ed9b09c5023465d71709dce089be503c7867fec68a93bcd7
|
||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||
CMAKE_ARGS
|
||||
-DPNG_SHARED=OFF
|
||||
-DPNG_TESTS=OFF
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
|
||||
BUILD_COMMAND msbuild /P:Configuration=Release INSTALL.vcxproj
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
if (${DEP_DEBUG})
|
||||
ExternalProject_Get_Property(dep_libpng BINARY_DIR)
|
||||
ExternalProject_Add_Step(dep_libpng build_debug
|
||||
DEPENDEES build
|
||||
DEPENDERS install
|
||||
COMMAND msbuild /P:Configuration=Debug INSTALL.vcxproj
|
||||
WORKING_DIRECTORY "${BINARY_DIR}"
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
if (${DEPS_BITS} EQUAL 32)
|
||||
set(DEP_LIBCURL_TARGET "x86")
|
||||
else ()
|
||||
set(DEP_LIBCURL_TARGET "x64")
|
||||
endif ()
|
||||
|
||||
ExternalProject_Add(dep_libcurl
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://curl.haxx.se/download/curl-7.58.0.tar.gz"
|
||||
URL_HASH SHA256=cc245bf9a1a42a45df491501d97d5593392a03f7b4f07b952793518d97666115
|
||||
BUILD_IN_SOURCE 1
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND cd winbuild && nmake /f Makefile.vc mode=static VC=12 GEN_PDB=yes DEBUG=no "MACHINE=${DEP_LIBCURL_TARGET}"
|
||||
INSTALL_COMMAND cd builds\\libcurl-*-release-*-winssl
|
||||
&& "${CMAKE_COMMAND}" -E copy_directory include "${DESTDIR}\\usr\\local\\include"
|
||||
&& "${CMAKE_COMMAND}" -E copy_directory lib "${DESTDIR}\\usr\\local\\lib"
|
||||
)
|
||||
if (${DEP_DEBUG})
|
||||
ExternalProject_Get_Property(dep_libcurl SOURCE_DIR)
|
||||
ExternalProject_Add_Step(dep_libcurl build_debug
|
||||
DEPENDEES build
|
||||
DEPENDERS install
|
||||
COMMAND cd winbuild && nmake /f Makefile.vc mode=static VC=12 GEN_PDB=yes DEBUG=yes "MACHINE=${DEP_LIBCURL_TARGET}"
|
||||
WORKING_DIRECTORY "${SOURCE_DIR}"
|
||||
)
|
||||
ExternalProject_Add_Step(dep_libcurl install_debug
|
||||
DEPENDEES install
|
||||
COMMAND cd builds\\libcurl-*-debug-*-winssl
|
||||
&& "${CMAKE_COMMAND}" -E copy_directory include "${DESTDIR}\\usr\\local\\include"
|
||||
&& "${CMAKE_COMMAND}" -E copy_directory lib "${DESTDIR}\\usr\\local\\lib"
|
||||
WORKING_DIRECTORY "${SOURCE_DIR}"
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
if (${DEPS_BITS} EQUAL 32)
|
||||
set(DEP_WXWIDGETS_TARGET "")
|
||||
set(DEP_WXWIDGETS_LIBDIR "vc_lib")
|
||||
else ()
|
||||
set(DEP_WXWIDGETS_TARGET "TARGET_CPU=X64")
|
||||
set(DEP_WXWIDGETS_LIBDIR "vc_x64_lib")
|
||||
endif ()
|
||||
|
||||
ExternalProject_Add(dep_wxwidgets
|
||||
EXCLUDE_FROM_ALL 1
|
||||
URL "https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.1/wxWidgets-3.1.1.tar.bz2"
|
||||
URL_HASH SHA256=c925dfe17e8f8b09eb7ea9bfdcfcc13696a3e14e92750effd839f5e10726159e
|
||||
BUILD_IN_SOURCE 1
|
||||
PATCH_COMMAND "${CMAKE_COMMAND}" -E copy "${CMAKE_CURRENT_SOURCE_DIR}\\wxwidgets-pngprefix.h" src\\png\\pngprefix.h
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND cd build\\msw && nmake /f makefile.vc BUILD=release SHARED=0 UNICODE=1 USE_GUI=1 "${DEP_WXWIDGETS_TARGET}"
|
||||
INSTALL_COMMAND "${CMAKE_COMMAND}" -E copy_directory include "${DESTDIR}\\usr\\local\\include"
|
||||
&& "${CMAKE_COMMAND}" -E copy_directory "lib\\${DEP_WXWIDGETS_LIBDIR}" "${DESTDIR}\\usr\\local\\lib\\${DEP_WXWIDGETS_LIBDIR}"
|
||||
)
|
||||
if (${DEP_DEBUG})
|
||||
ExternalProject_Get_Property(dep_wxwidgets SOURCE_DIR)
|
||||
ExternalProject_Add_Step(dep_wxwidgets build_debug
|
||||
DEPENDEES build
|
||||
DEPENDERS install
|
||||
COMMAND cd build\\msw && nmake /f makefile.vc BUILD=debug SHARED=0 UNICODE=1 USE_GUI=1 "${DEP_WXWIDGETS_TARGET}"
|
||||
WORKING_DIRECTORY "${SOURCE_DIR}"
|
||||
)
|
||||
endif ()
|
168
deps/wxwidgets-pngprefix.h
vendored
Normal file
|
@ -0,0 +1,168 @@
|
|||
// Patched in Slic3r: These two were missing:
|
||||
#define png_write_eXIf wx_png_write_eXIf
|
||||
#define png_handle_eXIf wx_png_handle_eXIf
|
||||
|
||||
#define png_sRGB_table wx_png_sRGB_table
|
||||
#define png_sRGB_base wx_png_sRGB_base
|
||||
#define png_sRGB_delta wx_png_sRGB_delta
|
||||
#define png_zstream_error wx_png_zstream_error
|
||||
#define png_free_buffer_list wx_png_free_buffer_list
|
||||
#define png_fixed wx_png_fixed
|
||||
#define png_user_version_check wx_png_user_version_check
|
||||
#define png_malloc_base wx_png_malloc_base
|
||||
#define png_malloc_array wx_png_malloc_array
|
||||
#define png_realloc_array wx_png_realloc_array
|
||||
#define png_create_png_struct wx_png_create_png_struct
|
||||
#define png_destroy_png_struct wx_png_destroy_png_struct
|
||||
#define png_free_jmpbuf wx_png_free_jmpbuf
|
||||
#define png_zalloc wx_png_zalloc
|
||||
#define png_zfree wx_png_zfree
|
||||
#define png_default_read_data wx_png_default_read_data
|
||||
#define png_push_fill_buffer wx_png_push_fill_buffer
|
||||
#define png_default_write_data wx_png_default_write_data
|
||||
#define png_default_flush wx_png_default_flush
|
||||
#define png_reset_crc wx_png_reset_crc
|
||||
#define png_write_data wx_png_write_data
|
||||
#define png_read_sig wx_png_read_sig
|
||||
#define png_read_chunk_header wx_png_read_chunk_header
|
||||
#define png_read_data wx_png_read_data
|
||||
#define png_crc_read wx_png_crc_read
|
||||
#define png_crc_finish wx_png_crc_finish
|
||||
#define png_crc_error wx_png_crc_error
|
||||
#define png_calculate_crc wx_png_calculate_crc
|
||||
#define png_flush wx_png_flush
|
||||
#define png_write_IHDR wx_png_write_IHDR
|
||||
#define png_write_PLTE wx_png_write_PLTE
|
||||
#define png_compress_IDAT wx_png_compress_IDAT
|
||||
#define png_write_IEND wx_png_write_IEND
|
||||
#define png_write_gAMA_fixed wx_png_write_gAMA_fixed
|
||||
#define png_write_sBIT wx_png_write_sBIT
|
||||
#define png_write_cHRM_fixed wx_png_write_cHRM_fixed
|
||||
#define png_write_sRGB wx_png_write_sRGB
|
||||
#define png_write_iCCP wx_png_write_iCCP
|
||||
#define png_write_sPLT wx_png_write_sPLT
|
||||
#define png_write_tRNS wx_png_write_tRNS
|
||||
#define png_write_bKGD wx_png_write_bKGD
|
||||
#define png_write_hIST wx_png_write_hIST
|
||||
#define png_write_tEXt wx_png_write_tEXt
|
||||
#define png_write_zTXt wx_png_write_zTXt
|
||||
#define png_write_iTXt wx_png_write_iTXt
|
||||
#define png_set_text_2 wx_png_set_text_2
|
||||
#define png_write_oFFs wx_png_write_oFFs
|
||||
#define png_write_pCAL wx_png_write_pCAL
|
||||
#define png_write_pHYs wx_png_write_pHYs
|
||||
#define png_write_tIME wx_png_write_tIME
|
||||
#define png_write_sCAL_s wx_png_write_sCAL_s
|
||||
#define png_write_finish_row wx_png_write_finish_row
|
||||
#define png_write_start_row wx_png_write_start_row
|
||||
#define png_combine_row wx_png_combine_row
|
||||
#define png_do_read_interlace wx_png_do_read_interlace
|
||||
#define png_do_write_interlace wx_png_do_write_interlace
|
||||
#define png_read_filter_row wx_png_read_filter_row
|
||||
#define png_write_find_filter wx_png_write_find_filter
|
||||
#define png_read_IDAT_data wx_png_read_IDAT_data
|
||||
#define png_read_finish_IDAT wx_png_read_finish_IDAT
|
||||
#define png_read_finish_row wx_png_read_finish_row
|
||||
#define png_read_start_row wx_png_read_start_row
|
||||
#define png_zlib_inflate wx_png_zlib_inflate
|
||||
#define png_read_transform_info wx_png_read_transform_info
|
||||
#define png_do_strip_channel wx_png_do_strip_channel
|
||||
#define png_do_swap wx_png_do_swap
|
||||
#define png_do_packswap wx_png_do_packswap
|
||||
#define png_do_invert wx_png_do_invert
|
||||
#define png_do_bgr wx_png_do_bgr
|
||||
#define png_handle_IHDR wx_png_handle_IHDR
|
||||
#define png_handle_PLTE wx_png_handle_PLTE
|
||||
#define png_handle_IEND wx_png_handle_IEND
|
||||
#define png_handle_bKGD wx_png_handle_bKGD
|
||||
#define png_handle_cHRM wx_png_handle_cHRM
|
||||
#define png_handle_gAMA wx_png_handle_gAMA
|
||||
#define png_handle_hIST wx_png_handle_hIST
|
||||
#define png_handle_iCCP wx_png_handle_iCCP
|
||||
#define png_handle_iTXt wx_png_handle_iTXt
|
||||
#define png_handle_oFFs wx_png_handle_oFFs
|
||||
#define png_handle_pCAL wx_png_handle_pCAL
|
||||
#define png_handle_pHYs wx_png_handle_pHYs
|
||||
#define png_handle_sBIT wx_png_handle_sBIT
|
||||
#define png_handle_sCAL wx_png_handle_sCAL
|
||||
#define png_handle_sPLT wx_png_handle_sPLT
|
||||
#define png_handle_sRGB wx_png_handle_sRGB
|
||||
#define png_handle_tEXt wx_png_handle_tEXt
|
||||
#define png_handle_tIME wx_png_handle_tIME
|
||||
#define png_handle_tRNS wx_png_handle_tRNS
|
||||
#define png_handle_zTXt wx_png_handle_zTXt
|
||||
#define png_check_chunk_name wx_png_check_chunk_name
|
||||
#define png_check_chunk_length wx_png_check_chunk_length
|
||||
#define png_handle_unknown wx_png_handle_unknown
|
||||
#define png_chunk_unknown_handling wx_png_chunk_unknown_handling
|
||||
#define png_do_read_transformations wx_png_do_read_transformations
|
||||
#define png_do_write_transformations wx_png_do_write_transformations
|
||||
#define png_init_read_transformations wx_png_init_read_transformations
|
||||
#define png_push_read_chunk wx_png_push_read_chunk
|
||||
#define png_push_read_sig wx_png_push_read_sig
|
||||
#define png_push_check_crc wx_png_push_check_crc
|
||||
#define png_push_save_buffer wx_png_push_save_buffer
|
||||
#define png_push_restore_buffer wx_png_push_restore_buffer
|
||||
#define png_push_read_IDAT wx_png_push_read_IDAT
|
||||
#define png_process_IDAT_data wx_png_process_IDAT_data
|
||||
#define png_push_process_row wx_png_push_process_row
|
||||
#define png_push_handle_unknown wx_png_push_handle_unknown
|
||||
#define png_push_have_info wx_png_push_have_info
|
||||
#define png_push_have_end wx_png_push_have_end
|
||||
#define png_push_have_row wx_png_push_have_row
|
||||
#define png_push_read_end wx_png_push_read_end
|
||||
#define png_process_some_data wx_png_process_some_data
|
||||
#define png_read_push_finish_row wx_png_read_push_finish_row
|
||||
#define png_push_handle_tEXt wx_png_push_handle_tEXt
|
||||
#define png_push_read_tEXt wx_png_push_read_tEXt
|
||||
#define png_push_handle_zTXt wx_png_push_handle_zTXt
|
||||
#define png_push_read_zTXt wx_png_push_read_zTXt
|
||||
#define png_push_handle_iTXt wx_png_push_handle_iTXt
|
||||
#define png_push_read_iTXt wx_png_push_read_iTXt
|
||||
#define png_colorspace_set_gamma wx_png_colorspace_set_gamma
|
||||
#define png_colorspace_sync_info wx_png_colorspace_sync_info
|
||||
#define png_colorspace_sync wx_png_colorspace_sync
|
||||
#define png_colorspace_set_chromaticities wx_png_colorspace_set_chromaticities
|
||||
#define png_colorspace_set_endpoints wx_png_colorspace_set_endpoints
|
||||
#define png_colorspace_set_sRGB wx_png_colorspace_set_sRGB
|
||||
#define png_colorspace_set_ICC wx_png_colorspace_set_ICC
|
||||
#define png_icc_check_length wx_png_icc_check_length
|
||||
#define png_icc_check_header wx_png_icc_check_header
|
||||
#define png_icc_check_tag_table wx_png_icc_check_tag_table
|
||||
#define png_icc_set_sRGB wx_png_icc_set_sRGB
|
||||
#define png_colorspace_set_rgb_coefficients wx_png_colorspace_set_rgb_coefficients
|
||||
#define png_check_IHDR wx_png_check_IHDR
|
||||
#define png_do_check_palette_indexes wx_png_do_check_palette_indexes
|
||||
#define png_fixed_error wx_png_fixed_error
|
||||
#define png_safecat wx_png_safecat
|
||||
#define png_format_number wx_png_format_number
|
||||
#define png_warning_parameter wx_png_warning_parameter
|
||||
#define png_warning_parameter_unsigned wx_png_warning_parameter_unsigned
|
||||
#define png_warning_parameter_signed wx_png_warning_parameter_signed
|
||||
#define png_formatted_warning wx_png_formatted_warning
|
||||
#define png_app_warning wx_png_app_warning
|
||||
#define png_app_error wx_png_app_error
|
||||
#define png_chunk_report wx_png_chunk_report
|
||||
#define png_ascii_from_fp wx_png_ascii_from_fp
|
||||
#define png_ascii_from_fixed wx_png_ascii_from_fixed
|
||||
#define png_check_fp_number wx_png_check_fp_number
|
||||
#define png_check_fp_string wx_png_check_fp_string
|
||||
#define png_muldiv wx_png_muldiv
|
||||
#define png_muldiv_warn wx_png_muldiv_warn
|
||||
#define png_reciprocal wx_png_reciprocal
|
||||
#define png_reciprocal2 wx_png_reciprocal2
|
||||
#define png_gamma_significant wx_png_gamma_significant
|
||||
#define png_gamma_correct wx_png_gamma_correct
|
||||
#define png_gamma_16bit_correct wx_png_gamma_16bit_correct
|
||||
#define png_gamma_8bit_correct wx_png_gamma_8bit_correct
|
||||
#define png_destroy_gamma_table wx_png_destroy_gamma_table
|
||||
#define png_build_gamma_table wx_png_build_gamma_table
|
||||
#define png_safe_error wx_png_safe_error
|
||||
#define png_safe_warning wx_png_safe_warning
|
||||
#define png_safe_execute wx_png_safe_execute
|
||||
#define png_image_error wx_png_image_error
|
||||
#define png_check_keyword wx_png_check_keyword
|
||||
|
||||
|
||||
|
||||
|
|
@ -26,10 +26,11 @@ TBB_SHA = a0dc9bf76d0120f917b641ed095360448cabc85b
|
|||
TBB = tbb-$(TBB_SHA)
|
||||
OPENSSL = openssl-OpenSSL_1_1_0g
|
||||
CURL = curl-7.58.0
|
||||
WXWIDGETS = wxWidgets-3.1.1
|
||||
|
||||
.PHONY: all destdir boost libcurl libopenssl libtbb
|
||||
|
||||
all: destdir boost libtbb libcurl
|
||||
all: destdir boost libtbb libcurl wxwidgets
|
||||
@echo
|
||||
@echo "All done!"
|
||||
@echo
|
||||
|
@ -131,5 +132,33 @@ $(CURL).tar.gz:
|
|||
curl -L -o $@ https://curl.haxx.se/download/$@
|
||||
|
||||
|
||||
|
||||
wxwidgets: $(WXWIDGETS).tar.bz2
|
||||
# tar -jxvf $(WXWIDGETS).tar.bz2
|
||||
cd $(WXWIDGETS) && ./configure \
|
||||
--prefix=$(DESTDIR)/usr/local \
|
||||
--disable-shared \
|
||||
--with-gtk=2 \
|
||||
--with-opengl \
|
||||
--enable-unicode \
|
||||
--enable-graphics_ctx \
|
||||
--with-regex=builtin \
|
||||
--with-libpng=builtin \
|
||||
--with-libxpm=builtin \
|
||||
--with-libjpeg=builtin \
|
||||
--with-libtiff=builtin \
|
||||
--with-zlib=builtin \
|
||||
--with-expat=builtin \
|
||||
--disable-precomp-headers \
|
||||
--enable-debug_info \
|
||||
--enable-debug_gdb
|
||||
$(MAKE) -C $(WXWIDGETS) -j$(NPROC)
|
||||
$(MAKE) -C $(WXWIDGETS)/locale allmo # XXX: ?
|
||||
$(MAKE) -C $(WXWIDGETS) install #DESTDIR=$(DESTDIR)
|
||||
|
||||
$(WXWIDGETS).tar.bz2:
|
||||
curl -L -o $@ https://github.com/wxWidgets/wxWidgets/releases/download/v3.1.1/$@
|
||||
|
||||
|
||||
clean:
|
||||
rm -rf $(BOOST) $(BOOST).tar.gz $(TBB) $(TBB).tar.gz $(OPENSSL) $(OPENSSL).tar.gz $(CURL) $(CURL).tar.gz
|
||||
|
|
|
@ -40,6 +40,8 @@ $BOOST = 'boost_1_63_0'
|
|||
$CURL = 'curl-7.58.0'
|
||||
$TBB_SHA = 'a0dc9bf76d0120f917b641ed095360448cabc85b'
|
||||
$TBB = "tbb-$TBB_SHA"
|
||||
$WXWIDGETS_VER = "3.1.1"
|
||||
$WXWIDGETS = "wxWidgets-$WXWIDGETS_VER"
|
||||
|
||||
|
||||
try
|
||||
|
@ -72,13 +74,21 @@ echo 'Downloading sources ...'
|
|||
if (!(Test-Path "$BOOST.zip")) { $webclient.DownloadFile("https://dl.bintray.com/boostorg/release/1.63.0/source/$BOOST.zip", "$BOOST.zip") }
|
||||
if (!(Test-Path "$TBB.zip")) { $webclient.DownloadFile("https://github.com/wjakob/tbb/archive/$TBB_SHA.zip", "$TBB.zip") }
|
||||
if (!(Test-Path "$CURL.zip")) { $webclient.DownloadFile("https://curl.haxx.se/download/$CURL.zip", ".\$CURL.zip") }
|
||||
if (!(Test-Path "$WXWIDGETS.zip")) { $webclient.DownloadFile("https://github.com/wxWidgets/wxWidgets/releases/download/v$WXWIDGETS_VER/$WXWIDGETS.zip", ".\$WXWIDGETS.zip") }
|
||||
|
||||
|
||||
# Unpack sources:
|
||||
echo 'Unpacking ...'
|
||||
if (!(Test-Path $BOOST)) { [IO.Compression.ZipFile]::ExtractToDirectory("$BOOST.zip", '.') }
|
||||
if (!(Test-Path $TBB)) { [IO.Compression.ZipFile]::ExtractToDirectory("$TBB.zip", '.') }
|
||||
if (!(Test-Path $CURL)) { [IO.Compression.ZipFile]::ExtractToDirectory("$CURL.zip", '.') }
|
||||
if (!(Test-Path "$BOOST")) { [IO.Compression.ZipFile]::ExtractToDirectory("$BOOST.zip", '.') }
|
||||
if (!(Test-Path "$TBB")) { [IO.Compression.ZipFile]::ExtractToDirectory("$TBB.zip", '.') }
|
||||
if (!(Test-Path "$CURL")) { [IO.Compression.ZipFile]::ExtractToDirectory("$CURL.zip", '.') }
|
||||
if (!(Test-Path "$WXWIDGETS")) { [IO.Compression.ZipFile]::ExtractToDirectory("$WXWIDGETS.zip", "$WXWIDGETS") }
|
||||
|
||||
|
||||
# Patch PNG in wxWidgets
|
||||
# PNG prefix is not applied properly to two functions
|
||||
$pngprefix_h = "$WXWIDGETS\src\png\pngprefix.h"
|
||||
"#define png_write_eXIf wx_png_write_eXIf`n#define png_handle_eXIf wx_png_handle_eXIf`n`n" + (Get-Content $pngprefix_h | Out-String) | Set-Content $pngprefix_h
|
||||
|
||||
|
||||
# Build libraries:
|
||||
|
@ -127,6 +137,22 @@ Copy-Item -R -Force ..\builds\libcurl-*-winssl\include\* "$destdir\usr\local\inc
|
|||
Copy-Item -R -Force ..\builds\libcurl-*-winssl\lib\* "$destdir\usr\local\lib\"
|
||||
popd
|
||||
|
||||
# Build wxWidgets
|
||||
pushd "$WXWIDGETS"
|
||||
pushd "build\msw"
|
||||
$target_cpu_opt = ("", "TARGET_CPU=X64")[!$b32]
|
||||
$lib_dir = ("vc_lib", "vc_x64_lib")[!$b32]
|
||||
nmake /f makefile.vc `
|
||||
BUILD=release `
|
||||
SHARED=0 `
|
||||
UNICODE=1 `
|
||||
USE_GUI=1 `
|
||||
"$target_cpu_opt"
|
||||
popd
|
||||
Copy-Item -R -Force include\* "$destdir\usr\local\include\"
|
||||
Copy-Item -R -Force "lib\$lib_dir" "$destdir\usr\local\lib\"
|
||||
popd
|
||||
|
||||
|
||||
echo ""
|
||||
echo "All done!"
|
||||
|
|
146
lib/Slic3r.pm
|
@ -1,5 +1,4 @@
|
|||
# This package loads all the non-GUI Slic3r perl packages.
|
||||
# In addition, it implements utility functions for file handling and threading.
|
||||
|
||||
package Slic3r;
|
||||
|
||||
|
@ -22,22 +21,11 @@ sub debugf {
|
|||
|
||||
our $loglevel = 0;
|
||||
|
||||
# load threads before Moo as required by it
|
||||
BEGIN {
|
||||
# Test, whether the perl was compiled with ithreads support and ithreads actually work.
|
||||
use Config;
|
||||
use Moo;
|
||||
my $have_threads = $Config{useithreads} && eval "use threads; use threads::shared; use Thread::Queue; 1";
|
||||
die "Slic3r Prusa Edition requires working Perl threads.\n" if ! $have_threads;
|
||||
die "threads.pm >= 1.96 is required, please update\n" if $threads::VERSION < 1.96;
|
||||
die "Perl threading is broken with this Moo version: " . $Moo::VERSION . "\n" if $Moo::VERSION == 1.003000;
|
||||
$debug = 1 if (defined($ENV{'SLIC3R_DEBUGOUT'}) && $ENV{'SLIC3R_DEBUGOUT'} == 1);
|
||||
print "Debugging output enabled\n" if $debug;
|
||||
}
|
||||
|
||||
warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n"
|
||||
if $^V == v5.16;
|
||||
|
||||
use FindBin;
|
||||
|
||||
# Let the XS module know where the GUI resources reside.
|
||||
|
@ -61,22 +49,15 @@ use Slic3r::Model;
|
|||
use Slic3r::Point;
|
||||
use Slic3r::Polygon;
|
||||
use Slic3r::Polyline;
|
||||
use Slic3r::Print;
|
||||
use Slic3r::Print::Object;
|
||||
use Slic3r::Print::Simple;
|
||||
use Slic3r::Surface;
|
||||
our $build = eval "use Slic3r::Build; 1";
|
||||
use Thread::Semaphore;
|
||||
|
||||
# Scaling between the float and integer coordinates.
|
||||
# Floats are in mm.
|
||||
use constant SCALING_FACTOR => 0.000001;
|
||||
|
||||
# Keep track of threads we created. Perl worker threads shall not create further threads.
|
||||
my @threads = ();
|
||||
my $pause_sema = Thread::Semaphore->new;
|
||||
my $paused = 0;
|
||||
|
||||
# Set the logging level at the Slic3r XS module.
|
||||
$Slic3r::loglevel = (defined($ENV{'SLIC3R_LOGLEVEL'}) && $ENV{'SLIC3R_LOGLEVEL'} =~ /^[1-9]/) ? $ENV{'SLIC3R_LOGLEVEL'} : 0;
|
||||
set_logging_level($Slic3r::loglevel);
|
||||
|
@ -85,128 +66,6 @@ set_logging_level($Slic3r::loglevel);
|
|||
# class instance in a thread safe manner.
|
||||
Slic3r::GCode::PlaceholderParser->new->evaluate_boolean_expression('1==1');
|
||||
|
||||
sub spawn_thread {
|
||||
my ($cb) = @_;
|
||||
@_ = ();
|
||||
my $thread = threads->create(sub {
|
||||
Slic3r::debugf "Starting thread %d...\n", threads->tid;
|
||||
local $SIG{'KILL'} = sub {
|
||||
Slic3r::debugf "Exiting thread %d...\n", threads->tid;
|
||||
Slic3r::thread_cleanup();
|
||||
threads->exit();
|
||||
};
|
||||
local $SIG{'STOP'} = sub {
|
||||
$pause_sema->down;
|
||||
$pause_sema->up;
|
||||
};
|
||||
$cb->();
|
||||
});
|
||||
push @threads, $thread->tid;
|
||||
return $thread;
|
||||
}
|
||||
|
||||
# call this at the very end of each thread (except the main one)
|
||||
# so that it does not try to free existing objects.
|
||||
# at that stage, existing objects are only those that we
|
||||
# inherited at the thread creation (thus shared) and those
|
||||
# that we are returning: destruction will be handled by the
|
||||
# main thread in both cases.
|
||||
# reminder: do not destroy inherited objects in other threads,
|
||||
# as the main thread will still try to destroy them when they
|
||||
# go out of scope; in other words, if you're undef()'ing an
|
||||
# object in a thread, make sure the main thread still holds a
|
||||
# reference so that it won't be destroyed in thread.
|
||||
sub thread_cleanup {
|
||||
# prevent destruction of shared objects
|
||||
no warnings 'redefine';
|
||||
*Slic3r::BridgeDetector::DESTROY = sub {};
|
||||
*Slic3r::Config::DESTROY = sub {};
|
||||
*Slic3r::Config::Full::DESTROY = sub {};
|
||||
*Slic3r::Config::GCode::DESTROY = sub {};
|
||||
*Slic3r::Config::Print::DESTROY = sub {};
|
||||
*Slic3r::Config::PrintObject::DESTROY = sub {};
|
||||
*Slic3r::Config::PrintRegion::DESTROY = sub {};
|
||||
*Slic3r::Config::Static::DESTROY = sub {};
|
||||
*Slic3r::ExPolygon::DESTROY = sub {};
|
||||
*Slic3r::ExPolygon::Collection::DESTROY = sub {};
|
||||
*Slic3r::ExtrusionLoop::DESTROY = sub {};
|
||||
*Slic3r::ExtrusionMultiPath::DESTROY = sub {};
|
||||
*Slic3r::ExtrusionPath::DESTROY = sub {};
|
||||
*Slic3r::ExtrusionPath::Collection::DESTROY = sub {};
|
||||
*Slic3r::ExtrusionSimulator::DESTROY = sub {};
|
||||
*Slic3r::Flow::DESTROY = sub {};
|
||||
*Slic3r::GCode::DESTROY = sub {};
|
||||
*Slic3r::GCode::PlaceholderParser::DESTROY = sub {};
|
||||
*Slic3r::GCode::PreviewData::DESTROY = sub {};
|
||||
*Slic3r::GCode::Sender::DESTROY = sub {};
|
||||
*Slic3r::Geometry::BoundingBox::DESTROY = sub {};
|
||||
*Slic3r::Geometry::BoundingBoxf::DESTROY = sub {};
|
||||
*Slic3r::Geometry::BoundingBoxf3::DESTROY = sub {};
|
||||
*Slic3r::Layer::PerimeterGenerator::DESTROY = sub {};
|
||||
*Slic3r::Line::DESTROY = sub {};
|
||||
*Slic3r::Linef3::DESTROY = sub {};
|
||||
*Slic3r::Model::DESTROY = sub {};
|
||||
*Slic3r::Model::Object::DESTROY = sub {};
|
||||
*Slic3r::Point::DESTROY = sub {};
|
||||
*Slic3r::Pointf::DESTROY = sub {};
|
||||
*Slic3r::Pointf3::DESTROY = sub {};
|
||||
*Slic3r::Polygon::DESTROY = sub {};
|
||||
*Slic3r::Polyline::DESTROY = sub {};
|
||||
*Slic3r::Polyline::Collection::DESTROY = sub {};
|
||||
*Slic3r::Print::DESTROY = sub {};
|
||||
*Slic3r::Print::Object::DESTROY = sub {};
|
||||
*Slic3r::Print::Region::DESTROY = sub {};
|
||||
*Slic3r::Surface::DESTROY = sub {};
|
||||
*Slic3r::Surface::Collection::DESTROY = sub {};
|
||||
*Slic3r::Print::SupportMaterial2::DESTROY = sub {};
|
||||
*Slic3r::TriangleMesh::DESTROY = sub {};
|
||||
*Slic3r::GUI::AppConfig::DESTROY = sub {};
|
||||
*Slic3r::GUI::GCodePreviewData::DESTROY = sub {};
|
||||
*Slic3r::GUI::PresetBundle::DESTROY = sub {};
|
||||
*Slic3r::GUI::Tab::DESTROY = sub {};
|
||||
*Slic3r::GUI::PresetHints::DESTROY = sub {};
|
||||
*Slic3r::GUI::TabIface::DESTROY = sub {};
|
||||
*Slic3r::OctoPrint::DESTROY = sub {};
|
||||
*Slic3r::Duet::DESTROY = sub {};
|
||||
*Slic3r::PresetUpdater::DESTROY = sub {};
|
||||
return undef; # this prevents a "Scalars leaked" warning
|
||||
}
|
||||
|
||||
sub _get_running_threads {
|
||||
return grep defined($_), map threads->object($_), @threads;
|
||||
}
|
||||
|
||||
sub kill_all_threads {
|
||||
# Send SIGKILL to all the running threads to let them die.
|
||||
foreach my $thread (_get_running_threads) {
|
||||
Slic3r::debugf "Thread %d killing %d...\n", threads->tid, $thread->tid;
|
||||
$thread->kill('KILL');
|
||||
}
|
||||
# unlock semaphore before we block on wait
|
||||
# otherwise we'd get a deadlock if threads were paused
|
||||
resume_all_threads();
|
||||
# in any thread we wait for our children
|
||||
foreach my $thread (_get_running_threads) {
|
||||
Slic3r::debugf " Thread %d waiting for %d...\n", threads->tid, $thread->tid;
|
||||
$thread->join; # block until threads are killed
|
||||
Slic3r::debugf " Thread %d finished waiting for %d...\n", threads->tid, $thread->tid;
|
||||
}
|
||||
@threads = ();
|
||||
}
|
||||
|
||||
sub pause_all_threads {
|
||||
return if $paused;
|
||||
$paused = 1;
|
||||
$pause_sema->down;
|
||||
$_->kill('STOP') for _get_running_threads;
|
||||
}
|
||||
|
||||
sub resume_all_threads {
|
||||
return unless $paused;
|
||||
$paused = 0;
|
||||
$pause_sema->up;
|
||||
}
|
||||
|
||||
# Open a file by converting $filename to local file system locales.
|
||||
sub open {
|
||||
my ($fh, $mode, $filename) = @_;
|
||||
|
@ -281,9 +140,4 @@ sub system_info
|
|||
return $out;
|
||||
}
|
||||
|
||||
# this package declaration prevents an ugly fatal warning to be emitted when
|
||||
# spawning a new thread
|
||||
package GLUquadricObjPtr;
|
||||
package Wx::Printout;
|
||||
|
||||
1;
|
||||
|
|
|
@ -7,18 +7,6 @@ use warnings;
|
|||
use List::Util qw(first);
|
||||
use Slic3r::Geometry::Clipper qw(union_ex diff_pl);
|
||||
|
||||
sub wkt {
|
||||
my $self = shift;
|
||||
return sprintf "POLYGON(%s)",
|
||||
join ',', map "($_)", map { join ',', map "$_->[0] $_->[1]", @$_ } @$self;
|
||||
}
|
||||
|
||||
sub dump_perl {
|
||||
my $self = shift;
|
||||
return sprintf "[%s]",
|
||||
join ',', map "[$_]", map { join ',', map "[$_->[0],$_->[1]]", @$_ } @$self;
|
||||
}
|
||||
|
||||
sub offset {
|
||||
my $self = shift;
|
||||
return Slic3r::Geometry::Clipper::offset(\@$self, @_);
|
||||
|
|
|
@ -6,25 +6,10 @@ use utf8;
|
|||
use File::Basename qw(basename);
|
||||
use FindBin;
|
||||
use List::Util qw(first);
|
||||
use Slic3r::GUI::2DBed;
|
||||
use Slic3r::GUI::Controller;
|
||||
use Slic3r::GUI::Controller::ManualControlDialog;
|
||||
use Slic3r::GUI::Controller::PrinterPanel;
|
||||
use Slic3r::GUI::MainFrame;
|
||||
use Slic3r::GUI::Plater;
|
||||
use Slic3r::GUI::Plater::2D;
|
||||
use Slic3r::GUI::Plater::2DToolpaths;
|
||||
use Slic3r::GUI::Plater::3D;
|
||||
use Slic3r::GUI::Plater::3DPreview;
|
||||
use Slic3r::GUI::Plater::ObjectPartsPanel;
|
||||
use Slic3r::GUI::Plater::ObjectCutDialog;
|
||||
use Slic3r::GUI::Plater::ObjectSettingsDialog;
|
||||
use Slic3r::GUI::Plater::LambdaObjectDialog;
|
||||
use Slic3r::GUI::Plater::OverrideSettingsPanel;
|
||||
use Slic3r::GUI::ProgressStatusBar;
|
||||
use Slic3r::GUI::OptionsGroup;
|
||||
use Slic3r::GUI::OptionsGroup::Field;
|
||||
use Slic3r::GUI::SystemInfo;
|
||||
|
||||
use Wx::Locale gettext => 'L';
|
||||
|
||||
|
@ -43,13 +28,11 @@ use constant FILE_WILDCARDS => {
|
|||
prusa => 'Prusa Control files (*.prusa)|*.prusa;*.PRUSA',
|
||||
ini => 'INI files *.ini|*.ini;*.INI',
|
||||
gcode => 'G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC',
|
||||
svg => 'SVG files *.svg|*.svg;*.SVG',
|
||||
};
|
||||
use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(known stl obj amf threemf prusa)};
|
||||
|
||||
# Datadir provided on the command line.
|
||||
our $datadir;
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
our $no_plater;
|
||||
our @cb;
|
||||
|
||||
|
@ -119,8 +102,6 @@ sub OnInit {
|
|||
print STDERR "Creating main frame...\n";
|
||||
Wx::Image::FindHandlerType(wxBITMAP_TYPE_PNG) || Wx::Image::AddHandler(Wx::PNGHandler->new);
|
||||
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
no_controller => $self->{app_config}->get('no_controller'),
|
||||
no_plater => $no_plater,
|
||||
lang_ch_event => $LANGUAGE_CHANGE_EVENT,
|
||||
preferences_event => $PREFERENCES_EVENT,
|
||||
|
@ -186,8 +167,6 @@ sub recreate_GUI{
|
|||
my ($self) = @_;
|
||||
my $topwindow = $self->GetTopWindow();
|
||||
$self->{mainframe} = my $frame = Slic3r::GUI::MainFrame->new(
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
no_controller => $self->{app_config}->get('no_controller'),
|
||||
no_plater => $no_plater,
|
||||
lang_ch_event => $LANGUAGE_CHANGE_EVENT,
|
||||
preferences_event => $PREFERENCES_EVENT,
|
||||
|
@ -226,16 +205,15 @@ sub system_info {
|
|||
$opengl_info = Slic3r::GUI::_3DScene::get_gl_info(1, 1);
|
||||
$opengl_info_txt = Slic3r::GUI::_3DScene::get_gl_info(0, 1);
|
||||
}
|
||||
my $about = Slic3r::GUI::SystemInfo->new(
|
||||
parent => undef,
|
||||
slic3r_info => $slic3r_info,
|
||||
# copyright_info => $copyright_info,
|
||||
system_info => $system_info,
|
||||
opengl_info => $opengl_info,
|
||||
text_info => Slic3r::slic3r_info . Slic3r::system_info . $opengl_info_txt,
|
||||
);
|
||||
$about->ShowModal;
|
||||
$about->Destroy;
|
||||
# my $about = Slic3r::GUI::SystemInfo->new(
|
||||
# parent => undef,
|
||||
# slic3r_info => $slic3r_info,
|
||||
# system_info => $system_info,
|
||||
# opengl_info => $opengl_info,
|
||||
# text_info => Slic3r::slic3r_info . Slic3r::system_info . $opengl_info_txt,
|
||||
# );
|
||||
# $about->ShowModal;
|
||||
# $about->Destroy;
|
||||
}
|
||||
|
||||
# static method accepting a wxWindow object as first parameter
|
||||
|
|
|
@ -1,216 +0,0 @@
|
|||
# Bed shape dialog
|
||||
|
||||
package Slic3r::GUI::2DBed;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use List::Util qw(min max);
|
||||
use Slic3r::Geometry qw(X Y unscale deg2rad);
|
||||
use Slic3r::Geometry::Clipper qw(intersection_pl);
|
||||
use Wx qw(:misc :pen :brush :font :systemsettings wxTAB_TRAVERSAL wxSOLID);
|
||||
use Wx::Event qw(EVT_PAINT EVT_ERASE_BACKGROUND EVT_MOUSE_EVENTS EVT_SIZE);
|
||||
use base qw(Wx::Panel Class::Accessor);
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(bed_shape interactive pos _scale_factor _shift on_move _painted));
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, $bed_shape) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [250,-1], wxTAB_TRAVERSAL);
|
||||
$self->{user_drawn_background} = $^O ne 'darwin';
|
||||
$self->bed_shape($bed_shape // []);
|
||||
EVT_PAINT($self, \&_repaint);
|
||||
EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background};
|
||||
EVT_MOUSE_EVENTS($self, \&_mouse_event);
|
||||
EVT_SIZE($self, sub { $self->Refresh; });
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub _repaint {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
my $dc = Wx::AutoBufferedPaintDC->new($self);
|
||||
my ($cw, $ch) = $self->GetSizeWH;
|
||||
return if $cw == 0; # when canvas is not rendered yet, size is 0,0
|
||||
|
||||
if ($self->{user_drawn_background}) {
|
||||
# On all systems the AutoBufferedPaintDC() achieves double buffering.
|
||||
# On MacOS the background is erased, on Windows the background is not erased
|
||||
# and on Linux/GTK the background is erased to gray color.
|
||||
# Fill DC with the background on Windows & Linux/GTK.
|
||||
my $color = Wx::SystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT);
|
||||
$dc->SetPen(Wx::Pen->new($color, 1, wxSOLID));
|
||||
$dc->SetBrush(Wx::Brush->new($color, wxSOLID));
|
||||
my $rect = $self->GetUpdateRegion()->GetBox();
|
||||
$dc->DrawRectangle($rect->GetLeft(), $rect->GetTop(), $rect->GetWidth(), $rect->GetHeight());
|
||||
}
|
||||
|
||||
# turn $cw and $ch from sizes to max coordinates
|
||||
$cw--;
|
||||
$ch--;
|
||||
|
||||
my $cbb = Slic3r::Geometry::BoundingBoxf->new_from_points([
|
||||
Slic3r::Pointf->new(0, 0),
|
||||
Slic3r::Pointf->new($cw, $ch),
|
||||
]);
|
||||
|
||||
# leave space for origin point
|
||||
$cbb->set_x_min($cbb->x_min + 4);
|
||||
$cbb->set_x_max($cbb->x_max - 4);
|
||||
$cbb->set_y_max($cbb->y_max - 4);
|
||||
|
||||
# leave space for origin label
|
||||
$cbb->set_y_max($cbb->y_max - 13);
|
||||
|
||||
# read new size
|
||||
($cw, $ch) = @{$cbb->size};
|
||||
my $ccenter = $cbb->center;
|
||||
|
||||
# get bounding box of bed shape in G-code coordinates
|
||||
my $bed_shape = $self->bed_shape;
|
||||
my $bed_polygon = Slic3r::Polygon->new_scale(@$bed_shape);
|
||||
my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($bed_shape);
|
||||
$bb->merge_point(Slic3r::Pointf->new(0,0)); # origin needs to be in the visible area
|
||||
my ($bw, $bh) = @{$bb->size};
|
||||
my $bcenter = $bb->center;
|
||||
|
||||
# calculate the scaling factor for fitting bed shape in canvas area
|
||||
my $sfactor = min($cw/$bw, $ch/$bh);
|
||||
my $shift = Slic3r::Pointf->new(
|
||||
$ccenter->x - $bcenter->x * $sfactor,
|
||||
$ccenter->y - $bcenter->y * $sfactor, #-
|
||||
);
|
||||
$self->_scale_factor($sfactor);
|
||||
$self->_shift(Slic3r::Pointf->new(
|
||||
$shift->x + $cbb->x_min,
|
||||
$shift->y - ($cbb->y_max-$self->GetSize->GetHeight), #++
|
||||
));
|
||||
|
||||
# draw bed fill
|
||||
{
|
||||
$dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,0,0), 1, wxSOLID));
|
||||
$dc->SetBrush(Wx::Brush->new(Wx::Colour->new(255,255,255), wxSOLID));
|
||||
$dc->DrawPolygon([ map $self->to_pixels($_), @$bed_shape ], 0, 0);
|
||||
}
|
||||
|
||||
# draw grid
|
||||
{
|
||||
my $step = 10; # 1cm grid
|
||||
my @polylines = ();
|
||||
for (my $x = $bb->x_min - ($bb->x_min % $step) + $step; $x < $bb->x_max; $x += $step) {
|
||||
push @polylines, Slic3r::Polyline->new_scale([$x, $bb->y_min], [$x, $bb->y_max]);
|
||||
}
|
||||
for (my $y = $bb->y_min - ($bb->y_min % $step) + $step; $y < $bb->y_max; $y += $step) {
|
||||
push @polylines, Slic3r::Polyline->new_scale([$bb->x_min, $y], [$bb->x_max, $y]);
|
||||
}
|
||||
@polylines = @{intersection_pl(\@polylines, [$bed_polygon])};
|
||||
|
||||
$dc->SetPen(Wx::Pen->new(Wx::Colour->new(230,230,230), 1, wxSOLID));
|
||||
$dc->DrawLine(map @{$self->to_pixels([map unscale($_), @$_])}, @$_[0,-1]) for @polylines;
|
||||
}
|
||||
|
||||
# draw bed contour
|
||||
{
|
||||
$dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,0,0), 1, wxSOLID));
|
||||
$dc->SetBrush(Wx::Brush->new(Wx::Colour->new(255,255,255), wxTRANSPARENT));
|
||||
$dc->DrawPolygon([ map $self->to_pixels($_), @$bed_shape ], 0, 0);
|
||||
}
|
||||
|
||||
my $origin_px = $self->to_pixels(Slic3r::Pointf->new(0,0));
|
||||
|
||||
# draw axes
|
||||
{
|
||||
my $axes_len = 50;
|
||||
my $arrow_len = 6;
|
||||
my $arrow_angle = deg2rad(45);
|
||||
$dc->SetPen(Wx::Pen->new(Wx::Colour->new(255,0,0), 2, wxSOLID)); # red
|
||||
my $x_end = Slic3r::Pointf->new($origin_px->[X] + $axes_len, $origin_px->[Y]);
|
||||
$dc->DrawLine(@$origin_px, @$x_end);
|
||||
foreach my $angle (-$arrow_angle, +$arrow_angle) {
|
||||
my $end = $x_end->clone;
|
||||
$end->translate(-$arrow_len, 0);
|
||||
$end->rotate($angle, $x_end);
|
||||
$dc->DrawLine(@$x_end, @$end);
|
||||
}
|
||||
|
||||
$dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,255,0), 2, wxSOLID)); # green
|
||||
my $y_end = Slic3r::Pointf->new($origin_px->[X], $origin_px->[Y] - $axes_len);
|
||||
$dc->DrawLine(@$origin_px, @$y_end);
|
||||
foreach my $angle (-$arrow_angle, +$arrow_angle) {
|
||||
my $end = $y_end->clone;
|
||||
$end->translate(0, +$arrow_len);
|
||||
$end->rotate($angle, $y_end);
|
||||
$dc->DrawLine(@$y_end, @$end);
|
||||
}
|
||||
}
|
||||
|
||||
# draw origin
|
||||
{
|
||||
$dc->SetPen(Wx::Pen->new(Wx::Colour->new(0,0,0), 1, wxSOLID));
|
||||
$dc->SetBrush(Wx::Brush->new(Wx::Colour->new(0,0,0), wxSOLID));
|
||||
$dc->DrawCircle(@$origin_px, 3);
|
||||
|
||||
$dc->SetTextForeground(Wx::Colour->new(0,0,0));
|
||||
$dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL));
|
||||
$dc->DrawText("(0,0)", $origin_px->[X] + 1, $origin_px->[Y] + 2);
|
||||
}
|
||||
|
||||
# draw current position
|
||||
if (defined $self->pos) {
|
||||
my $pos_px = $self->to_pixels($self->pos);
|
||||
$dc->SetPen(Wx::Pen->new(Wx::Colour->new(200,0,0), 2, wxSOLID));
|
||||
$dc->SetBrush(Wx::Brush->new(Wx::Colour->new(200,0,0), wxTRANSPARENT));
|
||||
$dc->DrawCircle(@$pos_px, 5);
|
||||
|
||||
$dc->DrawLine($pos_px->[X]-15, $pos_px->[Y], $pos_px->[X]+15, $pos_px->[Y]);
|
||||
$dc->DrawLine($pos_px->[X], $pos_px->[Y]-15, $pos_px->[X], $pos_px->[Y]+15);
|
||||
}
|
||||
|
||||
$self->_painted(1);
|
||||
}
|
||||
|
||||
sub _mouse_event {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
return if !$self->interactive;
|
||||
return if !$self->_painted;
|
||||
|
||||
my $pos = $event->GetPosition;
|
||||
my $point = $self->to_units([ $pos->x, $pos->y ]); #]]
|
||||
if ($event->LeftDown || $event->Dragging) {
|
||||
$self->on_move->($point) if $self->on_move;
|
||||
$self->Refresh;
|
||||
}
|
||||
}
|
||||
|
||||
# convert G-code coordinates into pixels
|
||||
sub to_pixels {
|
||||
my ($self, $point) = @_;
|
||||
|
||||
my $p = Slic3r::Pointf->new(@$point);
|
||||
$p->scale($self->_scale_factor);
|
||||
$p->translate(@{$self->_shift});
|
||||
return [$p->x, $self->GetSize->GetHeight - $p->y]; #]]
|
||||
}
|
||||
|
||||
# convert pixels into G-code coordinates
|
||||
sub to_units {
|
||||
my ($self, $point) = @_;
|
||||
|
||||
my $p = Slic3r::Pointf->new(
|
||||
$point->[X],
|
||||
$self->GetSize->GetHeight - $point->[Y],
|
||||
);
|
||||
$p->translate(@{$self->_shift->negative});
|
||||
$p->scale(1/$self->_scale_factor);
|
||||
return $p;
|
||||
}
|
||||
|
||||
sub set_pos {
|
||||
my ($self, $pos) = @_;
|
||||
|
||||
$self->pos($pos);
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,190 +0,0 @@
|
|||
# The "Controller" tab to control the printer using serial / USB.
|
||||
# This feature is rarely used. Much more often, the firmware reads the G-codes from a SD card.
|
||||
# May there be multiple subtabs per each printer connected?
|
||||
|
||||
package Slic3r::GUI::Controller;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog wxBORDER_NONE);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU);
|
||||
use base qw(Wx::ScrolledWindow Class::Accessor);
|
||||
use List::Util qw(first);
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(_selected_printer_preset));
|
||||
|
||||
our @ConfigOptions = qw(bed_shape serial_port serial_speed);
|
||||
|
||||
sub new {
|
||||
my ($class, $parent) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [600,350]);
|
||||
|
||||
$self->SetScrollbars(0, 1, 0, 1);
|
||||
$self->{sizer} = my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
|
||||
# warning to show when there are no printers configured
|
||||
{
|
||||
$self->{text_no_printers} = Wx::StaticText->new($self, -1,
|
||||
"No printers were configured for USB/serial control.",
|
||||
wxDefaultPosition, wxDefaultSize);
|
||||
$self->{sizer}->Add($self->{text_no_printers}, 0, wxTOP | wxLEFT, 30);
|
||||
}
|
||||
|
||||
# button for adding new printer panels
|
||||
{
|
||||
my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG),
|
||||
wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||
$btn->SetToolTipString("Add printer…")
|
||||
if $btn->can('SetToolTipString');
|
||||
|
||||
EVT_LEFT_DOWN($btn, sub {
|
||||
my $menu = Wx::Menu->new;
|
||||
my @panels = $self->print_panels;
|
||||
# remove printers that already exist
|
||||
# update configs of currently loaded print panels
|
||||
foreach my $preset (@{wxTheApp->{preset_bundle}->printer}) {
|
||||
my $preset_name = $preset->name;
|
||||
next if ! $preset->config->serial_port ||
|
||||
defined first { defined $_ && $_->printer_name eq $preset_name } @panels;
|
||||
my $myconfig = $preset->config->clone_only(\@ConfigOptions);
|
||||
my $id = &Wx::NewId();
|
||||
$menu->Append($id, $preset_name);
|
||||
EVT_MENU($menu, $id, sub {
|
||||
$self->add_printer($preset_name, $myconfig);
|
||||
});
|
||||
}
|
||||
$self->PopupMenu($menu, $btn->GetPosition);
|
||||
$menu->Destroy;
|
||||
});
|
||||
$self->{sizer}->Add($btn, 0, wxTOP | wxLEFT, 10);
|
||||
}
|
||||
|
||||
$self->SetSizer($sizer);
|
||||
$self->SetMinSize($self->GetSize);
|
||||
#$sizer->SetSizeHints($self);
|
||||
|
||||
EVT_CLOSE($self, sub {
|
||||
my (undef, $event) = @_;
|
||||
|
||||
if ($event->CanVeto) {
|
||||
foreach my $panel ($self->print_panels) {
|
||||
if ($panel->printing) {
|
||||
my $confirm = Wx::MessageDialog->new(
|
||||
$self, "Printer '" . $panel->printer_name . "' is printing.\n\nDo you want to stop printing?",
|
||||
'Unfinished Print', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION,
|
||||
);
|
||||
if ($confirm->ShowModal == wxID_NO) {
|
||||
$event->Veto;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach my $panel ($self->print_panels) {
|
||||
$panel->disconnect;
|
||||
}
|
||||
|
||||
$event->Skip;
|
||||
});
|
||||
|
||||
$self->Layout;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub OnActivate {
|
||||
my ($self) = @_;
|
||||
|
||||
# get all available presets
|
||||
my %presets = map { $_->name => $_->config->clone_only(\@ConfigOptions) }
|
||||
grep { $_->config->serial_port } @{wxTheApp->{preset_bundle}->printer};
|
||||
|
||||
# decide which ones we want to keep
|
||||
my %active = ();
|
||||
|
||||
# keep the ones that are currently connected or have jobs in queue
|
||||
$active{$_} = 1 for map $_->printer_name,
|
||||
grep { $_->is_connected || @{$_->jobs} > 0 }
|
||||
$self->print_panels;
|
||||
|
||||
if (%presets) {
|
||||
# if there are no active panels, use sensible defaults
|
||||
if (!%active && keys %presets <= 2) {
|
||||
# if only one or two presets exist, load them
|
||||
$active{$_} = 1 for keys %presets;
|
||||
}
|
||||
if (!%active) {
|
||||
# enable printers whose port is available
|
||||
my %ports = map { $_ => 1 } Slic3r::GUI::scan_serial_ports;
|
||||
$active{$_} = 1
|
||||
for grep exists $ports{$presets{$_}->serial_port}, keys %presets;
|
||||
}
|
||||
if (!%active && $self->_selected_printer_preset) {
|
||||
# enable currently selected printer if it is configured
|
||||
$active{$self->_selected_printer_preset} = 1
|
||||
if $presets{$self->_selected_printer_preset};
|
||||
}
|
||||
}
|
||||
|
||||
# apply changes
|
||||
for my $panel ($self->print_panels) {
|
||||
next if $active{$panel->printer_name};
|
||||
|
||||
$self->{sizer}->DetachWindow($panel);
|
||||
$panel->Destroy;
|
||||
}
|
||||
$self->add_printer($_, $presets{$_}) for sort keys %active;
|
||||
|
||||
# show/hide the warning about no printers
|
||||
$self->{text_no_printers}->Show(!%presets);
|
||||
|
||||
# show/hide the Add button
|
||||
$self->{btn_add}->Show(keys %presets != keys %active);
|
||||
|
||||
$self->Layout;
|
||||
|
||||
# we need this in order to trigger the OnSize event of wxScrolledWindow which
|
||||
# recalculates the virtual size
|
||||
Wx::GetTopLevelParent($self)->SendSizeEvent;
|
||||
}
|
||||
|
||||
sub add_printer {
|
||||
my ($self, $printer_name, $config) = @_;
|
||||
|
||||
# check that printer doesn't exist already
|
||||
foreach my $panel ($self->print_panels) {
|
||||
if ($panel->printer_name eq $printer_name) {
|
||||
return $panel;
|
||||
}
|
||||
}
|
||||
|
||||
my $printer_panel = Slic3r::GUI::Controller::PrinterPanel->new($self, $printer_name, $config);
|
||||
$self->{sizer}->Prepend($printer_panel, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
|
||||
$self->Layout;
|
||||
|
||||
return $printer_panel;
|
||||
}
|
||||
|
||||
sub print_panels {
|
||||
my ($self) = @_;
|
||||
return grep $_->isa('Slic3r::GUI::Controller::PrinterPanel'),
|
||||
map $_->GetWindow, $self->{sizer}->GetChildren;
|
||||
}
|
||||
|
||||
# Called by Slic3r::GUI::Tab::Printer::_on_presets_changed
|
||||
# when the presets are loaded or the user selects another preset.
|
||||
sub update_presets {
|
||||
my ($self, $presets) = @_;
|
||||
# update configs of currently loaded print panels
|
||||
my @presets = @$presets;
|
||||
foreach my $panel ($self->print_panels) {
|
||||
my $preset = $presets->find_preset($panel->printer_name, 0);
|
||||
$panel->config($preset->config->clone_only(\@ConfigOptions))
|
||||
if defined $preset;
|
||||
}
|
||||
|
||||
$self->_selected_printer_preset($presets->get_selected_preset->name);
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,190 +0,0 @@
|
|||
# A printer "Controller" -> "ManualControlDialog" subtab, opened per 3D printer connected?
|
||||
|
||||
package Slic3r::GUI::Controller::ManualControlDialog;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx qw(:dialog :id :misc :sizer :choicebook :button :bitmap
|
||||
wxBORDER_NONE wxTAB_TRAVERSAL);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_BUTTON);
|
||||
use base qw(Wx::Dialog Class::Accessor);
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(sender config2 x_homed y_homed));
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, $config, $sender) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent, -1, "Manual Control", wxDefaultPosition,
|
||||
[500,380], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||
$self->sender($sender);
|
||||
|
||||
$self->config2({
|
||||
xy_travel_speed => 130,
|
||||
z_travel_speed => 10,
|
||||
});
|
||||
|
||||
my $bed_sizer = Wx::FlexGridSizer->new(2, 3, 1, 1);
|
||||
$bed_sizer->AddGrowableCol(1, 1);
|
||||
$bed_sizer->AddGrowableRow(0, 1);
|
||||
|
||||
my $move_button = sub {
|
||||
my ($sizer, $label, $icon, $bold, $pos, $handler) = @_;
|
||||
|
||||
my $btn = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize,
|
||||
wxBU_LEFT | wxBU_EXACTFIT);
|
||||
$btn->SetFont($bold ? $Slic3r::GUI::small_bold_font : $Slic3r::GUI::small_font);
|
||||
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("$icon.png"), wxBITMAP_TYPE_PNG));
|
||||
$btn->SetBitmapPosition($pos);
|
||||
EVT_BUTTON($self, $btn, $handler);
|
||||
$sizer->Add($btn, 1, wxEXPAND | wxALL, 0);
|
||||
};
|
||||
|
||||
# Y buttons
|
||||
{
|
||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
for my $d (qw(+10 +1 +0.1)) {
|
||||
$move_button->($sizer, $d, 'arrow_up', 0, wxLEFT, sub { $self->rel_move('Y', $d) });
|
||||
}
|
||||
$move_button->($sizer, 'Y', 'house', 1, wxLEFT, sub { $self->home('Y') });
|
||||
for my $d (qw(-0.1 -1 -10)) {
|
||||
$move_button->($sizer, $d, 'arrow_down', 0, wxLEFT, sub { $self->rel_move('Y', $d) });
|
||||
};
|
||||
$bed_sizer->Add($sizer, 1, wxEXPAND, 0);
|
||||
}
|
||||
|
||||
# Bed canvas
|
||||
{
|
||||
my $bed_shape = $config->bed_shape;
|
||||
$self->{canvas} = my $canvas = Slic3r::GUI::2DBed->new($self, $bed_shape);
|
||||
$canvas->interactive(1);
|
||||
$canvas->on_move(sub {
|
||||
my ($pos) = @_;
|
||||
|
||||
if (!($self->x_homed && $self->y_homed)) {
|
||||
Slic3r::GUI::show_error($self, "Please home both X and Y before moving.");
|
||||
return ;
|
||||
}
|
||||
|
||||
# delete any pending commands to get a smoother movement
|
||||
$self->sender->purge_queue(1);
|
||||
$self->abs_xy_move($pos);
|
||||
});
|
||||
$bed_sizer->Add($canvas, 0, wxEXPAND | wxRIGHT, 3);
|
||||
}
|
||||
|
||||
# Z buttons
|
||||
{
|
||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
for my $d (qw(+10 +1 +0.1)) {
|
||||
$move_button->($sizer, $d, 'arrow_up', 0, wxLEFT, sub { $self->rel_move('Z', $d) });
|
||||
}
|
||||
$move_button->($sizer, 'Z', 'house', 1, wxLEFT, sub { $self->home('Z') });
|
||||
for my $d (qw(-0.1 -1 -10)) {
|
||||
$move_button->($sizer, $d, 'arrow_down', 0, wxLEFT, sub { $self->rel_move('Z', $d) });
|
||||
};
|
||||
$bed_sizer->Add($sizer, 1, wxEXPAND, 0);
|
||||
}
|
||||
|
||||
# XYZ home button
|
||||
$move_button->($bed_sizer, 'XYZ', 'house', 1, wxTOP, sub { $self->home(undef) });
|
||||
|
||||
# X buttons
|
||||
{
|
||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
for my $d (qw(-10 -1 -0.1)) {
|
||||
$move_button->($sizer, $d, 'arrow_left', 0, wxTOP, sub { $self->rel_move('X', $d) });
|
||||
}
|
||||
$move_button->($sizer, 'X', 'house', 1, wxTOP, sub { $self->home('X') });
|
||||
for my $d (qw(+0.1 +1 +10)) {
|
||||
$move_button->($sizer, $d, 'arrow_right', 0, wxTOP, sub { $self->rel_move('X', $d) });
|
||||
}
|
||||
$bed_sizer->Add($sizer, 1, wxEXPAND, 0);
|
||||
}
|
||||
|
||||
my $optgroup = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Settings',
|
||||
on_change => sub {
|
||||
my ($opt_id, $value) = @_;
|
||||
$self->config2->{$opt_id} = $value;
|
||||
},
|
||||
);
|
||||
{
|
||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
||||
label => 'Speed (mm/s)',
|
||||
);
|
||||
$line->append_option(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'xy_travel_speed',
|
||||
type => 'f',
|
||||
label => 'X/Y',
|
||||
tooltip => '',
|
||||
default => $self->config2->{xy_travel_speed},
|
||||
));
|
||||
$line->append_option(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'z_travel_speed',
|
||||
type => 'f',
|
||||
label => 'Z',
|
||||
tooltip => '',
|
||||
default => $self->config2->{z_travel_speed},
|
||||
));
|
||||
$optgroup->append_line($line);
|
||||
}
|
||||
|
||||
my $main_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$main_sizer->Add($bed_sizer, 1, wxEXPAND | wxALL, 10);
|
||||
$main_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10);
|
||||
#$main_sizer->Add($self->CreateButtonSizer(wxCLOSE), 0, wxEXPAND);
|
||||
#EVT_BUTTON($self, wxID_CLOSE, sub { $self->Close });
|
||||
|
||||
$self->SetSizer($main_sizer);
|
||||
$self->SetMinSize($self->GetSize);
|
||||
#$main_sizer->SetSizeHints($self);
|
||||
$self->Layout;
|
||||
|
||||
# needed to actually free memory
|
||||
EVT_CLOSE($self, sub {
|
||||
$self->EndModal(wxID_OK);
|
||||
$self->Destroy;
|
||||
});
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub abs_xy_move {
|
||||
my ($self, $pos) = @_;
|
||||
|
||||
$self->sender->send("G90", 1); # set absolute positioning
|
||||
$self->sender->send(sprintf("G1 X%.1f Y%.1f F%d", @$pos, $self->config2->{xy_travel_speed}*60), 1);
|
||||
$self->{canvas}->set_pos($pos);
|
||||
}
|
||||
|
||||
sub rel_move {
|
||||
my ($self, $axis, $distance) = @_;
|
||||
|
||||
my $speed = ($axis eq 'Z') ? $self->config2->{z_travel_speed} : $self->config2->{xy_travel_speed};
|
||||
$self->sender->send("G91", 1); # set relative positioning
|
||||
$self->sender->send(sprintf("G1 %s%.1f F%d", $axis, $distance, $speed*60), 1);
|
||||
$self->sender->send("G90", 1); # set absolute positioning
|
||||
|
||||
if (my $pos = $self->{canvas}->pos) {
|
||||
if ($axis eq 'X') {
|
||||
$pos->translate($distance, 0);
|
||||
} elsif ($axis eq 'Y') {
|
||||
$pos->translate(0, $distance);
|
||||
}
|
||||
$self->{canvas}->set_pos($pos);
|
||||
}
|
||||
}
|
||||
|
||||
sub home {
|
||||
my ($self, $axis) = @_;
|
||||
|
||||
$axis //= '';
|
||||
$self->sender->send(sprintf("G28 %s", $axis), 1);
|
||||
$self->{canvas}->set_pos(undef);
|
||||
$self->x_homed(1) if $axis eq 'X';
|
||||
$self->y_homed(1) if $axis eq 'Y';
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,707 +0,0 @@
|
|||
package Slic3r::GUI::Controller::PrinterPanel;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :window :gauge :timer
|
||||
:textctrl :font :systemsettings);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_MOUSEWHEEL EVT_TIMER EVT_SCROLLWIN);
|
||||
use base qw(Wx::Panel Class::Accessor);
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(printer_name config sender jobs
|
||||
printing status_timer temp_timer));
|
||||
|
||||
use constant CONNECTION_TIMEOUT => 3; # seconds
|
||||
use constant STATUS_TIMER_INTERVAL => 1000; # milliseconds
|
||||
use constant TEMP_TIMER_INTERVAL => 5000; # milliseconds
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, $printer_name, $config) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [500, 250]);
|
||||
|
||||
$self->printer_name($printer_name || 'Printer');
|
||||
$self->config($config);
|
||||
$self->jobs([]);
|
||||
|
||||
# set up the timer that polls for updates
|
||||
{
|
||||
my $timer_id = &Wx::NewId();
|
||||
$self->status_timer(Wx::Timer->new($self, $timer_id));
|
||||
EVT_TIMER($self, $timer_id, sub {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
if ($self->printing) {
|
||||
my $queue_size = $self->sender->queue_size;
|
||||
$self->{gauge}->SetValue($self->{gauge}->GetRange - $queue_size);
|
||||
if ($queue_size == 0) {
|
||||
$self->print_completed;
|
||||
}
|
||||
}
|
||||
$self->{log_textctrl}->AppendText("$_\n") for @{$self->sender->purge_log};
|
||||
{
|
||||
my $temp = $self->sender->getT;
|
||||
if ($temp eq '') {
|
||||
$self->{temp_panel}->Hide;
|
||||
} else {
|
||||
if (!$self->{temp_panel}->IsShown) {
|
||||
$self->{temp_panel}->Show;
|
||||
$self->Layout;
|
||||
}
|
||||
$self->{temp_text}->SetLabel($temp . "°C");
|
||||
|
||||
$temp = $self->sender->getB;
|
||||
if ($temp eq '') {
|
||||
$self->{bed_temp_text}->SetLabel('n.a.');
|
||||
} else {
|
||||
$self->{bed_temp_text}->SetLabel($temp . "°C");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
# set up the timer that sends temperature requests
|
||||
# (responses are handled by status_timer)
|
||||
{
|
||||
my $timer_id = &Wx::NewId();
|
||||
$self->temp_timer(Wx::Timer->new($self, $timer_id));
|
||||
EVT_TIMER($self, $timer_id, sub {
|
||||
my ($self, $event) = @_;
|
||||
$self->sender->send("M105", 1); # send it through priority queue
|
||||
});
|
||||
}
|
||||
|
||||
my $box = Wx::StaticBox->new($self, -1, "");
|
||||
my $sizer = Wx::StaticBoxSizer->new($box, wxHORIZONTAL);
|
||||
my $left_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
|
||||
# printer name
|
||||
{
|
||||
my $text = Wx::StaticText->new($box, -1, $self->printer_name, wxDefaultPosition, [220,-1]);
|
||||
my $font = $text->GetFont;
|
||||
$font->SetPointSize(20);
|
||||
$text->SetFont($font);
|
||||
$left_sizer->Add($text, 0, wxEXPAND, 0);
|
||||
}
|
||||
|
||||
# connection info
|
||||
{
|
||||
my $conn_sizer = Wx::FlexGridSizer->new(2, 2, 1, 0);
|
||||
$conn_sizer->SetFlexibleDirection(wxHORIZONTAL);
|
||||
$conn_sizer->AddGrowableCol(1, 1);
|
||||
$left_sizer->Add($conn_sizer, 0, wxEXPAND | wxTOP, 5);
|
||||
{
|
||||
my $text = Wx::StaticText->new($box, -1, "Port:", wxDefaultPosition, wxDefaultSize);
|
||||
$text->SetFont($Slic3r::GUI::small_font);
|
||||
$conn_sizer->Add($text, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
|
||||
}
|
||||
my $serial_port_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
{
|
||||
$self->{serial_port_combobox} = Wx::ComboBox->new($box, -1, $config->serial_port, wxDefaultPosition, wxDefaultSize, []);
|
||||
$self->{serial_port_combobox}->SetFont($Slic3r::GUI::small_font);
|
||||
$self->update_serial_ports;
|
||||
$serial_port_sizer->Add($self->{serial_port_combobox}, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 1);
|
||||
}
|
||||
{
|
||||
$self->{btn_rescan_serial} = my $btn = Wx::BitmapButton->new($box, -1, Wx::Bitmap->new(Slic3r::var("arrow_rotate_clockwise.png"), wxBITMAP_TYPE_PNG),
|
||||
wxDefaultPosition, wxDefaultSize, &Wx::wxBORDER_NONE);
|
||||
$btn->SetToolTipString("Rescan serial ports")
|
||||
if $btn->can('SetToolTipString');
|
||||
$serial_port_sizer->Add($btn, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
EVT_BUTTON($self, $btn, sub { $self->update_serial_ports });
|
||||
}
|
||||
$conn_sizer->Add($serial_port_sizer, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
|
||||
|
||||
{
|
||||
my $text = Wx::StaticText->new($box, -1, "Speed:", wxDefaultPosition, wxDefaultSize);
|
||||
$text->SetFont($Slic3r::GUI::small_font);
|
||||
$conn_sizer->Add($text, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
|
||||
}
|
||||
my $serial_speed_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
{
|
||||
$self->{serial_speed_combobox} = Wx::ComboBox->new($box, -1, $config->serial_speed, wxDefaultPosition, wxDefaultSize,
|
||||
["115200", "250000"]);
|
||||
$self->{serial_speed_combobox}->SetFont($Slic3r::GUI::small_font);
|
||||
$serial_speed_sizer->Add($self->{serial_speed_combobox}, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
}
|
||||
{
|
||||
$self->{btn_disconnect} = my $btn = Wx::Button->new($box, -1, "Disconnect", wxDefaultPosition, wxDefaultSize);
|
||||
$btn->SetFont($Slic3r::GUI::small_font);
|
||||
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG));
|
||||
$serial_speed_sizer->Add($btn, 0, wxLEFT, 5);
|
||||
EVT_BUTTON($self, $btn, \&disconnect);
|
||||
}
|
||||
$conn_sizer->Add($serial_speed_sizer, 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5);
|
||||
}
|
||||
|
||||
# buttons
|
||||
{
|
||||
$self->{btn_connect} = my $btn = Wx::Button->new($box, -1, "Connect to printer", wxDefaultPosition, [-1, 40]);
|
||||
my $font = $btn->GetFont;
|
||||
$font->SetPointSize($font->GetPointSize + 2);
|
||||
$btn->SetFont($font);
|
||||
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("arrow_up.png"), wxBITMAP_TYPE_PNG));
|
||||
$left_sizer->Add($btn, 0, wxTOP, 15);
|
||||
EVT_BUTTON($self, $btn, \&connect);
|
||||
}
|
||||
|
||||
# print progress bar
|
||||
{
|
||||
my $gauge = $self->{gauge} = Wx::Gauge->new($self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize);
|
||||
$left_sizer->Add($self->{gauge}, 0, wxEXPAND | wxTOP, 15);
|
||||
$gauge->Hide;
|
||||
}
|
||||
|
||||
# status
|
||||
$self->{status_text} = Wx::StaticText->new($box, -1, "", wxDefaultPosition, [200,-1]);
|
||||
$left_sizer->Add($self->{status_text}, 1, wxEXPAND | wxTOP, 15);
|
||||
|
||||
# manual control
|
||||
{
|
||||
$self->{btn_manual_control} = my $btn = Wx::Button->new($box, -1, "Manual control", wxDefaultPosition, wxDefaultSize);
|
||||
$btn->SetFont($Slic3r::GUI::small_font);
|
||||
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("cog.png"), wxBITMAP_TYPE_PNG));
|
||||
$btn->Hide;
|
||||
$left_sizer->Add($btn, 0, wxTOP, 15);
|
||||
EVT_BUTTON($self, $btn, sub {
|
||||
my $dlg = Slic3r::GUI::Controller::ManualControlDialog->new
|
||||
($self, $self->config, $self->sender);
|
||||
$dlg->ShowModal;
|
||||
});
|
||||
}
|
||||
|
||||
# temperature
|
||||
{
|
||||
my $temp_panel = $self->{temp_panel} = Wx::Panel->new($box, -1);
|
||||
my $temp_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
|
||||
my $temp_font = Wx::Font->new($Slic3r::GUI::small_font);
|
||||
$temp_font->SetWeight(wxFONTWEIGHT_BOLD);
|
||||
{
|
||||
my $text = Wx::StaticText->new($temp_panel, -1, "Temperature:", wxDefaultPosition, wxDefaultSize);
|
||||
$text->SetFont($Slic3r::GUI::small_font);
|
||||
$temp_sizer->Add($text, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
$self->{temp_text} = Wx::StaticText->new($temp_panel, -1, "", wxDefaultPosition, wxDefaultSize);
|
||||
$self->{temp_text}->SetFont($temp_font);
|
||||
$self->{temp_text}->SetForegroundColour(Wx::wxRED);
|
||||
$temp_sizer->Add($self->{temp_text}, 1, wxALIGN_CENTER_VERTICAL);
|
||||
}
|
||||
{
|
||||
my $text = Wx::StaticText->new($temp_panel, -1, "Bed:", wxDefaultPosition, wxDefaultSize);
|
||||
$text->SetFont($Slic3r::GUI::small_font);
|
||||
$temp_sizer->Add($text, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
$self->{bed_temp_text} = Wx::StaticText->new($temp_panel, -1, "", wxDefaultPosition, wxDefaultSize);
|
||||
$self->{bed_temp_text}->SetFont($temp_font);
|
||||
$self->{bed_temp_text}->SetForegroundColour(Wx::wxRED);
|
||||
$temp_sizer->Add($self->{bed_temp_text}, 1, wxALIGN_CENTER_VERTICAL);
|
||||
}
|
||||
$temp_panel->SetSizer($temp_sizer);
|
||||
$temp_panel->Hide;
|
||||
$left_sizer->Add($temp_panel, 0, wxEXPAND | wxTOP | wxBOTTOM, 4);
|
||||
}
|
||||
|
||||
# print jobs panel
|
||||
$self->{print_jobs_sizer} = my $print_jobs_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
{
|
||||
my $text = Wx::StaticText->new($box, -1, "Queue:", wxDefaultPosition, wxDefaultSize);
|
||||
$text->SetFont($Slic3r::GUI::small_font);
|
||||
$print_jobs_sizer->Add($text, 0, wxEXPAND, 0);
|
||||
|
||||
$self->{jobs_panel} = Wx::ScrolledWindow->new($box, -1, wxDefaultPosition, wxDefaultSize,
|
||||
wxVSCROLL | wxBORDER_NONE);
|
||||
$self->{jobs_panel}->SetScrollbars(0, 1, 0, 1);
|
||||
$self->{jobs_panel_sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$self->{jobs_panel}->SetSizer($self->{jobs_panel_sizer});
|
||||
$print_jobs_sizer->Add($self->{jobs_panel}, 1, wxEXPAND, 0);
|
||||
|
||||
# TODO: fix this. We're trying to pass the scroll event to the parent but it
|
||||
# doesn't work.
|
||||
EVT_SCROLLWIN($self->{jobs_panel}, sub {
|
||||
my ($panel, $event) = @_;
|
||||
|
||||
my $controller = $self->GetParent;
|
||||
my $new_event = Wx::ScrollWinEvent->new(
|
||||
$event->GetEventType,
|
||||
$event->GetPosition,
|
||||
$event->GetOrientation,
|
||||
);
|
||||
$controller->ProcessEvent($new_event);
|
||||
}) if 0;
|
||||
}
|
||||
|
||||
my $log_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
{
|
||||
my $text = Wx::StaticText->new($box, -1, "Log:", wxDefaultPosition, wxDefaultSize);
|
||||
$text->SetFont($Slic3r::GUI::small_font);
|
||||
$log_sizer->Add($text, 0, wxEXPAND, 0);
|
||||
|
||||
my $log = $self->{log_textctrl} = Wx::TextCtrl->new($box, -1, "", wxDefaultPosition, wxDefaultSize,
|
||||
wxTE_MULTILINE | wxBORDER_NONE);
|
||||
$log->SetBackgroundColour($box->GetBackgroundColour);
|
||||
$log->SetFont($Slic3r::GUI::small_font);
|
||||
$log->SetEditable(0);
|
||||
$log_sizer->Add($self->{log_textctrl}, 1, wxEXPAND, 0);
|
||||
}
|
||||
|
||||
$sizer->Add($left_sizer, 0, wxEXPAND | wxALL, 0);
|
||||
$sizer->Add($print_jobs_sizer, 2, wxEXPAND | wxALL, 0);
|
||||
$sizer->Add($log_sizer, 1, wxEXPAND | wxLEFT, 15);
|
||||
|
||||
$self->SetSizer($sizer);
|
||||
$self->SetMinSize($self->GetSize);
|
||||
|
||||
$self->_update_connection_controls;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub is_connected {
|
||||
my ($self) = @_;
|
||||
return $self->sender && $self->sender->is_connected;
|
||||
}
|
||||
|
||||
sub _update_connection_controls {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->{btn_connect}->Show;
|
||||
$self->{btn_disconnect}->Hide;
|
||||
$self->{serial_port_combobox}->Enable;
|
||||
$self->{serial_speed_combobox}->Enable;
|
||||
$self->{btn_rescan_serial}->Enable;
|
||||
$self->{btn_manual_control}->Hide;
|
||||
$self->{btn_manual_control}->Disable;
|
||||
|
||||
if ($self->is_connected) {
|
||||
$self->{btn_connect}->Hide;
|
||||
$self->{btn_manual_control}->Show;
|
||||
if (!$self->printing || $self->printing->paused) {
|
||||
$self->{btn_disconnect}->Show;
|
||||
$self->{btn_manual_control}->Enable;
|
||||
}
|
||||
$self->{serial_port_combobox}->Disable;
|
||||
$self->{serial_speed_combobox}->Disable;
|
||||
$self->{btn_rescan_serial}->Disable;
|
||||
}
|
||||
|
||||
$self->Layout;
|
||||
}
|
||||
|
||||
sub set_status {
|
||||
my ($self, $status) = @_;
|
||||
$self->{status_text}->SetLabel($status);
|
||||
$self->{status_text}->Wrap($self->{status_text}->GetSize->GetWidth);
|
||||
$self->{status_text}->Refresh;
|
||||
$self->Layout;
|
||||
}
|
||||
|
||||
sub connect {
|
||||
my ($self) = @_;
|
||||
|
||||
return if $self->is_connected;
|
||||
|
||||
$self->set_status("Connecting...");
|
||||
$self->sender(Slic3r::GCode::Sender->new);
|
||||
my $res = $self->sender->connect(
|
||||
$self->{serial_port_combobox}->GetValue,
|
||||
$self->{serial_speed_combobox}->GetValue,
|
||||
);
|
||||
if (!$res) {
|
||||
$self->set_status("Connection failed. Check serial port and speed.");
|
||||
} else {
|
||||
if ($self->sender->wait_connected) {
|
||||
$self->set_status("Printer is online. You can now start printing from the queue on the right.");
|
||||
$self->status_timer->Start(STATUS_TIMER_INTERVAL, wxTIMER_CONTINUOUS);
|
||||
$self->temp_timer->Start(TEMP_TIMER_INTERVAL, wxTIMER_CONTINUOUS);
|
||||
|
||||
# request temperature now, without waiting for the timer
|
||||
$self->sender->send("M105", 1);
|
||||
} else {
|
||||
$self->set_status("Connection failed. Check serial port and speed.");
|
||||
}
|
||||
}
|
||||
$self->_update_connection_controls;
|
||||
$self->reload_jobs;
|
||||
}
|
||||
|
||||
sub disconnect {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->status_timer->Stop;
|
||||
$self->temp_timer->Stop;
|
||||
return if !$self->is_connected;
|
||||
|
||||
$self->printing->printing(0) if $self->printing;
|
||||
$self->printing(undef);
|
||||
$self->{gauge}->Hide;
|
||||
$self->{temp_panel}->Hide;
|
||||
$self->sender->disconnect;
|
||||
$self->set_status("");
|
||||
$self->_update_connection_controls;
|
||||
$self->reload_jobs;
|
||||
}
|
||||
|
||||
sub update_serial_ports {
|
||||
my ($self) = @_;
|
||||
|
||||
my $cb = $self->{serial_port_combobox};
|
||||
my $current = $cb->GetValue;
|
||||
$cb->Clear;
|
||||
$cb->Append($_) for Slic3r::GUI::scan_serial_ports;
|
||||
$cb->SetValue($current);
|
||||
}
|
||||
|
||||
sub load_print_job {
|
||||
my ($self, $gcode_file, $filament_stats) = @_;
|
||||
|
||||
push @{$self->jobs}, my $job = Slic3r::GUI::Controller::PrinterPanel::PrintJob->new(
|
||||
id => time() . $gcode_file . rand(1000),
|
||||
gcode_file => $gcode_file,
|
||||
filament_stats => $filament_stats,
|
||||
);
|
||||
$self->reload_jobs;
|
||||
return $job;
|
||||
}
|
||||
|
||||
sub delete_job {
|
||||
my ($self, $job) = @_;
|
||||
|
||||
$self->jobs([ grep $_->id ne $job->id, @{$self->jobs} ]);
|
||||
$self->reload_jobs;
|
||||
}
|
||||
|
||||
sub print_job {
|
||||
my ($self, $job) = @_;
|
||||
|
||||
$self->printing($job);
|
||||
$job->printing(1);
|
||||
$self->reload_jobs;
|
||||
|
||||
open my $fh, '<', $job->gcode_file;
|
||||
my $line_count = 0;
|
||||
while (my $row = <$fh>) {
|
||||
$self->sender->send($row);
|
||||
$line_count++;
|
||||
}
|
||||
close $fh;
|
||||
|
||||
$self->_update_connection_controls;
|
||||
$self->{gauge}->SetRange($line_count);
|
||||
$self->{gauge}->SetValue(0);
|
||||
$self->{gauge}->Enable;
|
||||
$self->{gauge}->Show;
|
||||
$self->Layout;
|
||||
|
||||
$self->set_status('Printing...');
|
||||
$self->{log_textctrl}->AppendText(sprintf "=====\n");
|
||||
$self->{log_textctrl}->AppendText(sprintf "Printing %s\n", $job->name);
|
||||
$self->{log_textctrl}->AppendText(sprintf "Print started at %s\n", $self->_timestamp);
|
||||
}
|
||||
|
||||
sub print_completed {
|
||||
my ($self) = @_;
|
||||
|
||||
my $job = $self->printing;
|
||||
$self->printing(undef);
|
||||
$job->printing(0);
|
||||
$job->printed(1);
|
||||
$self->_update_connection_controls;
|
||||
$self->{gauge}->Hide;
|
||||
$self->Layout;
|
||||
|
||||
$self->set_status('Print completed.');
|
||||
$self->{log_textctrl}->AppendText(sprintf "Print completed at %s\n", $self->_timestamp);
|
||||
|
||||
$self->reload_jobs;
|
||||
}
|
||||
|
||||
sub reload_jobs {
|
||||
my ($self) = @_;
|
||||
|
||||
# reorder jobs
|
||||
@{$self->jobs} = sort { ($a->printed <=> $b->printed) || ($a->timestamp <=> $b->timestamp) }
|
||||
@{$self->jobs};
|
||||
|
||||
# remove all panels
|
||||
foreach my $child ($self->{jobs_panel_sizer}->GetChildren) {
|
||||
my $window = $child->GetWindow;
|
||||
$self->{jobs_panel_sizer}->Detach($window);
|
||||
# now $child does not exist anymore
|
||||
$window->Destroy;
|
||||
}
|
||||
|
||||
# re-add all panels
|
||||
foreach my $job (@{$self->jobs}) {
|
||||
my $panel = Slic3r::GUI::Controller::PrinterPanel::PrintJobPanel->new($self->{jobs_panel}, $job);
|
||||
$self->{jobs_panel_sizer}->Add($panel, 0, wxEXPAND | wxBOTTOM, 5);
|
||||
|
||||
$panel->on_delete_job(sub {
|
||||
my ($job) = @_;
|
||||
$self->delete_job($job);
|
||||
});
|
||||
$panel->on_print_job(sub {
|
||||
my ($job) = @_;
|
||||
$self->print_job($job);
|
||||
});
|
||||
$panel->on_pause_print(sub {
|
||||
my ($job) = @_;
|
||||
$self->sender->pause_queue;
|
||||
$job->paused(1);
|
||||
$self->reload_jobs;
|
||||
$self->_update_connection_controls;
|
||||
$self->{gauge}->Disable;
|
||||
$self->set_status('Print is paused. Click on Resume to continue.');
|
||||
});
|
||||
$panel->on_abort_print(sub {
|
||||
my ($job) = @_;
|
||||
$self->sender->purge_queue;
|
||||
$self->printing(undef);
|
||||
$job->printing(0);
|
||||
$job->paused(0);
|
||||
$self->reload_jobs;
|
||||
$self->_update_connection_controls;
|
||||
$self->{gauge}->Disable;
|
||||
$self->{gauge}->Hide;
|
||||
$self->set_status('Print was aborted.');
|
||||
$self->{log_textctrl}->AppendText(sprintf "Print aborted at %s\n", $self->_timestamp);
|
||||
});
|
||||
$panel->on_resume_print(sub {
|
||||
my ($job) = @_;
|
||||
$self->sender->resume_queue;
|
||||
$job->paused(0);
|
||||
$self->reload_jobs;
|
||||
$self->_update_connection_controls;
|
||||
$self->{gauge}->Enable;
|
||||
$self->set_status('Printing...');
|
||||
});
|
||||
$panel->enable_print if $self->is_connected && !$self->printing;
|
||||
|
||||
EVT_MOUSEWHEEL($panel, sub {
|
||||
my (undef, $event) = @_;
|
||||
Wx::PostEvent($self->{jobs_panel}, $event);
|
||||
$event->Skip;
|
||||
});
|
||||
}
|
||||
|
||||
$self->{jobs_panel}->Layout;
|
||||
$self->{print_jobs_sizer}->Layout;
|
||||
}
|
||||
|
||||
sub _timestamp {
|
||||
my ($self) = @_;
|
||||
|
||||
my @time = localtime(time);
|
||||
return sprintf '%02d:%02d:%02d', @time[2,1,0];
|
||||
}
|
||||
|
||||
package Slic3r::GUI::Controller::PrinterPanel::PrintJob;
|
||||
use Moo;
|
||||
|
||||
use File::Basename qw(basename);
|
||||
|
||||
has 'id' => (is => 'ro', required => 1);
|
||||
has 'timestamp' => (is => 'ro', default => sub { time });
|
||||
has 'gcode_file' => (is => 'ro', required => 1);
|
||||
has 'filament_stats' => (is => 'rw');
|
||||
has 'printing' => (is => 'rw', default => sub { 0 });
|
||||
has 'paused' => (is => 'rw', default => sub { 0 });
|
||||
has 'printed' => (is => 'rw', default => sub { 0 });
|
||||
|
||||
sub name {
|
||||
my ($self) = @_;
|
||||
return basename($self->gcode_file);
|
||||
}
|
||||
|
||||
package Slic3r::GUI::Controller::PrinterPanel::PrintJobPanel;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx qw(wxTheApp :panel :id :misc :sizer :button :bitmap :font :dialog :icon :timer
|
||||
:colour :brush :pen);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_TIMER EVT_ERASE_BACKGROUND);
|
||||
use base qw(Wx::Panel Class::Accessor);
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(job on_delete_job on_print_job on_pause_print on_resume_print
|
||||
on_abort_print blink_timer));
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, $job) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize);
|
||||
|
||||
$self->job($job);
|
||||
$self->SetBackgroundColour(wxWHITE);
|
||||
|
||||
{
|
||||
my $white_brush = Wx::Brush->new(wxWHITE, wxSOLID);
|
||||
my $pen = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID);
|
||||
EVT_ERASE_BACKGROUND($self, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $dc = $event->GetDC;
|
||||
my $size = $self->GetSize;
|
||||
$dc->SetBrush($white_brush);
|
||||
$dc->SetPen($pen);
|
||||
$dc->DrawRoundedRectangle(0, 0, $size->GetWidth,$size->GetHeight, 6);
|
||||
});
|
||||
}
|
||||
|
||||
my $left_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
{
|
||||
$self->{job_name_textctrl} = my $text = Wx::StaticText->new($self, -1, $job->name, wxDefaultPosition, wxDefaultSize);
|
||||
my $font = $text->GetFont;
|
||||
$font->SetWeight(wxFONTWEIGHT_BOLD);
|
||||
$text->SetFont($font);
|
||||
if ($job->printed) {
|
||||
$text->SetForegroundColour($Slic3r::GUI::grey);
|
||||
}
|
||||
$left_sizer->Add($text, 0, wxEXPAND, 0);
|
||||
}
|
||||
{
|
||||
my $filament_stats = join "\n",
|
||||
map "$_ (" . sprintf("%.2f", $job->filament_stats->{$_}/1000) . "m)",
|
||||
sort keys %{$job->filament_stats};
|
||||
my $text = Wx::StaticText->new($self, -1, $filament_stats, wxDefaultPosition, wxDefaultSize);
|
||||
$text->SetFont($Slic3r::GUI::small_font);
|
||||
if ($job->printed && !$job->printing) {
|
||||
$text->SetForegroundColour($Slic3r::GUI::grey);
|
||||
}
|
||||
$left_sizer->Add($text, 0, wxEXPAND | wxTOP, 6);
|
||||
}
|
||||
|
||||
my $buttons_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
my $button_style = Wx::wxBORDER_NONE | wxBU_EXACTFIT;
|
||||
{
|
||||
my $btn = $self->{btn_delete} = Wx::Button->new($self, -1, 'Delete',
|
||||
wxDefaultPosition, wxDefaultSize, $button_style);
|
||||
$btn->SetToolTipString("Delete this job from print queue")
|
||||
if $btn->can('SetToolTipString');
|
||||
$btn->SetFont($Slic3r::GUI::small_font);
|
||||
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG));
|
||||
if ($job->printing) {
|
||||
$btn->Hide;
|
||||
}
|
||||
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
|
||||
|
||||
EVT_BUTTON($self, $btn, sub {
|
||||
my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete this print job?", 'Delete Job', wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION)->ShowModal;
|
||||
return unless $res == wxID_YES;
|
||||
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->on_delete_job->($job);
|
||||
});
|
||||
});
|
||||
}
|
||||
{
|
||||
my $label = $job->printed ? 'Print Again' : 'Print This';
|
||||
my $btn = $self->{btn_print} = Wx::Button->new($self, -1, $label, wxDefaultPosition, wxDefaultSize,
|
||||
$button_style);
|
||||
$btn->SetFont($Slic3r::GUI::small_bold_font);
|
||||
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG));
|
||||
$btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG));
|
||||
#$btn->SetBitmapPosition(wxRIGHT);
|
||||
$btn->Hide;
|
||||
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
|
||||
|
||||
EVT_BUTTON($self, $btn, sub {
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->on_print_job->($job);
|
||||
});
|
||||
});
|
||||
}
|
||||
{
|
||||
my $btn = $self->{btn_pause} = Wx::Button->new($self, -1, "Pause", wxDefaultPosition, wxDefaultSize,
|
||||
$button_style);
|
||||
$btn->SetFont($Slic3r::GUI::small_font);
|
||||
if (!$job->printing || $job->paused) {
|
||||
$btn->Hide;
|
||||
}
|
||||
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_pause.png"), wxBITMAP_TYPE_PNG));
|
||||
$btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_pause_blue.png"), wxBITMAP_TYPE_PNG));
|
||||
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
|
||||
|
||||
EVT_BUTTON($self, $btn, sub {
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->on_pause_print->($job);
|
||||
});
|
||||
});
|
||||
}
|
||||
{
|
||||
my $btn = $self->{btn_resume} = Wx::Button->new($self, -1, "Resume", wxDefaultPosition, wxDefaultSize,
|
||||
$button_style);
|
||||
$btn->SetFont($Slic3r::GUI::small_font);
|
||||
if (!$job->printing || !$job->paused) {
|
||||
$btn->Hide;
|
||||
}
|
||||
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_play.png"), wxBITMAP_TYPE_PNG));
|
||||
$btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_play_blue.png"), wxBITMAP_TYPE_PNG));
|
||||
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
|
||||
|
||||
EVT_BUTTON($self, $btn, sub {
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->on_resume_print->($job);
|
||||
});
|
||||
});
|
||||
}
|
||||
{
|
||||
my $btn = $self->{btn_abort} = Wx::Button->new($self, -1, "Abort", wxDefaultPosition, wxDefaultSize,
|
||||
$button_style);
|
||||
$btn->SetFont($Slic3r::GUI::small_font);
|
||||
if (!$job->printing) {
|
||||
$btn->Hide;
|
||||
}
|
||||
$btn->SetBitmap(Wx::Bitmap->new(Slic3r::var("control_stop.png"), wxBITMAP_TYPE_PNG));
|
||||
$btn->SetBitmapCurrent(Wx::Bitmap->new(Slic3r::var("control_stop_blue.png"), wxBITMAP_TYPE_PNG));
|
||||
$buttons_sizer->Add($btn, 0, wxBOTTOM, 2);
|
||||
|
||||
EVT_BUTTON($self, $btn, sub {
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->on_abort_print->($job);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$sizer->Add($left_sizer, 1, wxEXPAND | wxALL, 6);
|
||||
$sizer->Add($buttons_sizer, 0, wxEXPAND | wxALL, 6);
|
||||
$self->SetSizer($sizer);
|
||||
|
||||
# set-up the timer that changes the job name color while printing
|
||||
if ($self->job->printing && !$self->job->paused) {
|
||||
my $timer_id = &Wx::NewId();
|
||||
$self->blink_timer(Wx::Timer->new($self, $timer_id));
|
||||
my $blink = 0; # closure
|
||||
my $colour = Wx::Colour->new(0, 190, 0);
|
||||
EVT_TIMER($self, $timer_id, sub {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
$self->{job_name_textctrl}->SetForegroundColour($blink ? Wx::wxBLACK : $colour);
|
||||
$blink = !$blink;
|
||||
});
|
||||
$self->blink_timer->Start(1000, wxTIMER_CONTINUOUS);
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub enable_print {
|
||||
my ($self) = @_;
|
||||
|
||||
if (!$self->job->printing) {
|
||||
$self->{btn_print}->Show;
|
||||
}
|
||||
$self->Layout;
|
||||
}
|
||||
|
||||
sub Destroy {
|
||||
my ($self) = @_;
|
||||
|
||||
# There's a gap between the time Perl destroys the wxPanel object and
|
||||
# the blink_timer member, so the wxTimer might still fire an event which
|
||||
# isn't handled properly, causing a crash. So we ensure that blink_timer
|
||||
# is stopped before we destroy the wxPanel.
|
||||
$self->blink_timer->Stop if $self->blink_timer;
|
||||
return $self->SUPER::Destroy;
|
||||
}
|
||||
|
||||
1;
|
|
@ -9,7 +9,7 @@ use File::Basename qw(basename dirname);
|
|||
use FindBin;
|
||||
use List::Util qw(min first);
|
||||
use Slic3r::Geometry qw(X Y);
|
||||
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog
|
||||
use Wx qw(:frame :bitmap :id :misc :notebook :panel :sizer :menu :dialog :filedialog :dirdialog
|
||||
:font :icon wxTheApp);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_COMMAND EVT_MENU EVT_NOTEBOOK_PAGE_CHANGED);
|
||||
use base 'Wx::Frame';
|
||||
|
@ -26,6 +26,18 @@ our $appController;
|
|||
our $VALUE_CHANGE_EVENT = Wx::NewEventType;
|
||||
# 2) To inform about a preset selection change or a "modified" status change.
|
||||
our $PRESETS_CHANGED_EVENT = Wx::NewEventType;
|
||||
# 3) To update the status bar with the progress information.
|
||||
our $PROGRESS_BAR_EVENT = Wx::NewEventType;
|
||||
# 4) To display an error dialog box from a thread on the UI thread.
|
||||
our $ERROR_EVENT = Wx::NewEventType;
|
||||
# 5) To inform about a change of object selection
|
||||
our $OBJECT_SELECTION_CHANGED_EVENT = Wx::NewEventType;
|
||||
# 6) To inform about a change of object settings
|
||||
our $OBJECT_SETTINGS_CHANGED_EVENT = Wx::NewEventType;
|
||||
# 7) To inform about a remove of object
|
||||
our $OBJECT_REMOVE_EVENT = Wx::NewEventType;
|
||||
# 8) To inform about a update of the scene
|
||||
our $UPDATE_SCENE_EVENT = Wx::NewEventType;
|
||||
|
||||
sub new {
|
||||
my ($class, %params) = @_;
|
||||
|
@ -45,8 +57,6 @@ sub new {
|
|||
}
|
||||
|
||||
# store input params
|
||||
# If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden.
|
||||
$self->{no_controller} = $params{no_controller};
|
||||
$self->{no_plater} = $params{no_plater};
|
||||
$self->{loaded} = 0;
|
||||
$self->{lang_ch_event} = $params{lang_ch_event};
|
||||
|
@ -62,15 +72,12 @@ sub new {
|
|||
eval { Wx::ToolTip::SetAutoPop(32767) };
|
||||
|
||||
# initialize status bar
|
||||
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, Wx::NewId);
|
||||
$self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new();
|
||||
$self->{statusbar}->Embed;
|
||||
$self->{statusbar}->SetStatusText(L("Version ").$Slic3r::VERSION.L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases"));
|
||||
$self->SetStatusBar($self->{statusbar});
|
||||
|
||||
# Make the global status bar and its progress indicator available in C++
|
||||
$appController->set_global_progress_indicator(
|
||||
$self->{statusbar}->{prog}->GetId(),
|
||||
$self->{statusbar}->GetId(),
|
||||
);
|
||||
Slic3r::GUI::set_progress_status_bar($self->{statusbar});
|
||||
$appController->set_global_progress_indicator($self->{statusbar});
|
||||
|
||||
$appController->set_model($self->{plater}->{model});
|
||||
$appController->set_print($self->{plater}->{print});
|
||||
|
@ -105,6 +112,7 @@ sub new {
|
|||
# Save the slic3r.ini. Usually the ini file is saved from "on idle" callback,
|
||||
# but in rare cases it may not have been called yet.
|
||||
wxTheApp->{app_config}->save;
|
||||
$self->{statusbar}->ResetCancelCallback();
|
||||
$self->{plater}->{print} = undef if($self->{plater});
|
||||
Slic3r::GUI::_3DScene::remove_all_canvases();
|
||||
Slic3r::GUI::deregister_on_request_update_callback();
|
||||
|
@ -114,6 +122,8 @@ sub new {
|
|||
|
||||
$self->update_ui_from_settings;
|
||||
|
||||
Slic3r::GUI::update_mode();
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
|
@ -133,10 +143,12 @@ sub _init_tabpanel {
|
|||
});
|
||||
|
||||
if (!$self->{no_plater}) {
|
||||
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), L("Plater"));
|
||||
if (!$self->{no_controller}) {
|
||||
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), L("Controller"));
|
||||
}
|
||||
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel,
|
||||
event_object_selection_changed => $OBJECT_SELECTION_CHANGED_EVENT,
|
||||
event_object_settings_changed => $OBJECT_SETTINGS_CHANGED_EVENT,
|
||||
event_remove_object => $OBJECT_REMOVE_EVENT,
|
||||
event_update_scene => $UPDATE_SCENE_EVENT,
|
||||
), L("Plater"));
|
||||
}
|
||||
|
||||
#TODO this is an example of a Slic3r XS interface call to add a new preset editor page to the main view.
|
||||
|
@ -154,6 +166,10 @@ sub _init_tabpanel {
|
|||
my $value = $event->GetInt();
|
||||
$self->{plater}->on_extruders_change($value);
|
||||
}
|
||||
if ($opt_key eq 'printer_technology'){
|
||||
my $value = $event->GetInt();# 0 ~ "ptFFF"; 1 ~ "ptSLA"
|
||||
$self->{plater}->show_preset_comboboxes($value);
|
||||
}
|
||||
}
|
||||
# don't save while loading for the first time
|
||||
$self->config->save($Slic3r::GUI::autosave) if $Slic3r::GUI::autosave && $self->{loaded};
|
||||
|
@ -166,7 +182,7 @@ sub _init_tabpanel {
|
|||
|
||||
my $tab = Slic3r::GUI::get_preset_tab($tab_name);
|
||||
if ($self->{plater}) {
|
||||
# Update preset combo boxes (Print settings, Filament, Printer) from their respective tabs.
|
||||
# Update preset combo boxes (Print settings, Filament, Material, Printer) from their respective tabs.
|
||||
my $presets = $tab->get_presets;
|
||||
if (defined $presets){
|
||||
my $reload_dependent_tabs = $tab->get_dependent_tabs;
|
||||
|
@ -174,26 +190,77 @@ sub _init_tabpanel {
|
|||
$self->{plater}->{"selected_item_$tab_name"} = $tab->get_selected_preset_item;
|
||||
if ($tab_name eq 'printer') {
|
||||
# Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
|
||||
for my $tab_name_other (qw(print filament)) {
|
||||
for my $tab_name_other (qw(print filament sla_material)) {
|
||||
# If the printer tells us that the print or filament preset has been switched or invalidated,
|
||||
# refresh the print or filament tab page. Otherwise just refresh the combo box.
|
||||
my $update_action = ($reload_dependent_tabs && (first { $_ eq $tab_name_other } (@{$reload_dependent_tabs})))
|
||||
? 'load_current_preset' : 'update_tab_ui';
|
||||
$self->{options_tabs}{$tab_name_other}->$update_action;
|
||||
}
|
||||
# Update the controller printers.
|
||||
$self->{controller}->update_presets($presets) if $self->{controller};
|
||||
}
|
||||
$self->{plater}->on_config_change($tab->get_config);
|
||||
}
|
||||
}
|
||||
});
|
||||
Slic3r::GUI::create_preset_tabs($self->{no_controller}, $VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT);
|
||||
|
||||
# The following event is emited by the C++ Tab implementation on object selection change.
|
||||
EVT_COMMAND($self, -1, $OBJECT_SELECTION_CHANGED_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my $obj_idx = $event->GetId;
|
||||
# my $child = $event->GetInt == 1 ? 1 : undef;
|
||||
# $self->{plater}->select_object($obj_idx < 0 ? undef: $obj_idx, $child);
|
||||
# $self->{plater}->item_changed_selection($obj_idx);
|
||||
|
||||
my $vol_idx = $event->GetInt;
|
||||
$self->{plater}->select_object_from_cpp($obj_idx < 0 ? undef: $obj_idx, $vol_idx<0 ? -1 : $vol_idx);
|
||||
});
|
||||
|
||||
# The following event is emited by the C++ GUI implementation on object settings change.
|
||||
EVT_COMMAND($self, -1, $OBJECT_SETTINGS_CHANGED_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
my $line = $event->GetString;
|
||||
my ($obj_idx, $parts_changed, $part_settings_changed) = split('',$line);
|
||||
|
||||
$self->{plater}->changed_object_settings($obj_idx, $parts_changed, $part_settings_changed);
|
||||
});
|
||||
|
||||
# The following event is emited by the C++ GUI implementation on object remove.
|
||||
EVT_COMMAND($self, -1, $OBJECT_REMOVE_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
$self->{plater}->remove();
|
||||
});
|
||||
|
||||
# The following event is emited by the C++ GUI implementation on extruder change for object.
|
||||
EVT_COMMAND($self, -1, $UPDATE_SCENE_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
$self->{plater}->update();
|
||||
});
|
||||
|
||||
Slic3r::GUI::create_preset_tabs($VALUE_CHANGE_EVENT, $PRESETS_CHANGED_EVENT);
|
||||
$self->{options_tabs} = {};
|
||||
for my $tab_name (qw(print filament printer)) {
|
||||
for my $tab_name (qw(print filament sla_material printer)) {
|
||||
$self->{options_tabs}{$tab_name} = Slic3r::GUI::get_preset_tab("$tab_name");
|
||||
}
|
||||
|
||||
# Update progress bar with an event sent by the slicing core.
|
||||
EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
if (defined $self->{progress_dialog}) {
|
||||
# If a progress dialog is open, update it.
|
||||
$self->{progress_dialog}->Update($event->GetInt, $event->GetString . "…");
|
||||
} else {
|
||||
# Otherwise update the main window status bar.
|
||||
$self->{statusbar}->SetProgress($event->GetInt);
|
||||
$self->{statusbar}->SetStatusText($event->GetString . "…");
|
||||
}
|
||||
});
|
||||
|
||||
EVT_COMMAND($self, -1, $ERROR_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
Slic3r::GUI::show_error($self, $event->GetString);
|
||||
});
|
||||
|
||||
if ($self->{plater}) {
|
||||
$self->{plater}->on_select_preset(sub {
|
||||
my ($group, $name) = @_;
|
||||
|
@ -202,8 +269,14 @@ sub _init_tabpanel {
|
|||
# load initial config
|
||||
my $full_config = wxTheApp->{preset_bundle}->full_config;
|
||||
$self->{plater}->on_config_change($full_config);
|
||||
|
||||
# Show a correct number of filament fields.
|
||||
$self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter}));
|
||||
if (defined $full_config->nozzle_diameter){ # nozzle_diameter is undefined when SLA printer is selected
|
||||
$self->{plater}->on_extruders_change(int(@{$full_config->nozzle_diameter}));
|
||||
}
|
||||
|
||||
# Show correct preset comboboxes according to the printer_technology
|
||||
$self->{plater}->show_preset_comboboxes(($full_config->printer_technology eq "FFF") ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,28 +302,8 @@ sub _init_menubar {
|
|||
$self->export_configbundle;
|
||||
}, undef, 'lorry_go.png');
|
||||
$fileMenu->AppendSeparator();
|
||||
my $repeat;
|
||||
$self->_append_menu_item($fileMenu, L("Q&uick Slice…\tCtrl+U"), L('Slice a file into a G-code'), sub {
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->quick_slice;
|
||||
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
|
||||
});
|
||||
}, undef, 'cog_go.png');
|
||||
$self->_append_menu_item($fileMenu, L("Quick Slice and Save &As…\tCtrl+Alt+U"), L('Slice a file into a G-code, save as'), sub {
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->quick_slice(save_as => 1);
|
||||
$repeat->Enable(defined $Slic3r::GUI::MainFrame::last_input_file);
|
||||
});
|
||||
}, undef, 'cog_go.png');
|
||||
$repeat = $self->_append_menu_item($fileMenu, L("&Repeat Last Quick Slice\tCtrl+Shift+U"), L('Repeat last quick slice'), sub {
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->quick_slice(reslice => 1);
|
||||
});
|
||||
}, undef, 'cog_go.png');
|
||||
$repeat->Enable(0);
|
||||
$fileMenu->AppendSeparator();
|
||||
$self->_append_menu_item($fileMenu, L("Slice to SV&G…\tCtrl+G"), L('Slice file to a multi-layer SVG'), sub {
|
||||
$self->quick_slice(save_as => 1, export_svg => 1);
|
||||
$self->_append_menu_item($fileMenu, L("Slice to PNG…"), L('Slice file to a set of PNG files'), sub {
|
||||
$self->slice_to_png;
|
||||
}, undef, 'shape_handles.png');
|
||||
$self->{menu_item_reslice_now} = $self->_append_menu_item(
|
||||
$fileMenu, L("(&Re)Slice Now\tCtrl+S"), L('Start new slicing process'),
|
||||
|
@ -397,116 +450,11 @@ sub on_plater_selection_changed {
|
|||
for $self->{object_menu}->GetMenuItems;
|
||||
}
|
||||
|
||||
# To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
|
||||
sub quick_slice {
|
||||
my ($self, %params) = @_;
|
||||
|
||||
my $progress_dialog;
|
||||
eval {
|
||||
# validate configuration
|
||||
my $config = wxTheApp->{preset_bundle}->full_config();
|
||||
$config->validate;
|
||||
|
||||
# select input file
|
||||
my $input_file;
|
||||
if (!$params{reslice}) {
|
||||
my $dialog = Wx::FileDialog->new($self, L('Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):'),
|
||||
wxTheApp->{app_config}->get_last_dir, "",
|
||||
&Slic3r::GUI::MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
if ($dialog->ShowModal != wxID_OK) {
|
||||
$dialog->Destroy;
|
||||
return;
|
||||
}
|
||||
$input_file = $dialog->GetPaths;
|
||||
$dialog->Destroy;
|
||||
$qs_last_input_file = $input_file unless $params{export_svg};
|
||||
} else {
|
||||
if (!defined $qs_last_input_file) {
|
||||
Wx::MessageDialog->new($self, L("No previously sliced file."),
|
||||
L('Error'), wxICON_ERROR | wxOK)->ShowModal();
|
||||
return;
|
||||
}
|
||||
if (! -e $qs_last_input_file) {
|
||||
Wx::MessageDialog->new($self, L("Previously sliced file (").$qs_last_input_file.L(") not found."),
|
||||
L('File Not Found'), wxICON_ERROR | wxOK)->ShowModal();
|
||||
return;
|
||||
}
|
||||
$input_file = $qs_last_input_file;
|
||||
}
|
||||
my $input_file_basename = basename($input_file);
|
||||
wxTheApp->{app_config}->update_skein_dir(dirname($input_file));
|
||||
|
||||
my $print_center;
|
||||
{
|
||||
my $bed_shape = Slic3r::Polygon->new_scale(@{$config->bed_shape});
|
||||
$print_center = Slic3r::Pointf->new_unscale(@{$bed_shape->bounding_box->center});
|
||||
}
|
||||
|
||||
my $sprint = Slic3r::Print::Simple->new(
|
||||
print_center => $print_center,
|
||||
status_cb => sub {
|
||||
my ($percent, $message) = @_;
|
||||
$progress_dialog->Update($percent, "$message…");
|
||||
},
|
||||
);
|
||||
|
||||
# keep model around
|
||||
my $model = Slic3r::Model->read_from_file($input_file);
|
||||
|
||||
$sprint->apply_config($config);
|
||||
$sprint->set_model($model);
|
||||
|
||||
# Copy the names of active presets into the placeholder parser.
|
||||
wxTheApp->{preset_bundle}->export_selections_pp($sprint->placeholder_parser);
|
||||
|
||||
# select output file
|
||||
my $output_file;
|
||||
if ($params{reslice}) {
|
||||
$output_file = $qs_last_output_file if defined $qs_last_output_file;
|
||||
} elsif ($params{save_as}) {
|
||||
# The following line may die if the output_filename_format template substitution fails.
|
||||
$output_file = $sprint->output_filepath;
|
||||
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/ if $params{export_svg};
|
||||
my $dlg = Wx::FileDialog->new($self, L('Save ') . ($params{export_svg} ? L('SVG') : L('G-code')) . L(' file as:'),
|
||||
wxTheApp->{app_config}->get_last_output_dir(dirname($output_file)),
|
||||
basename($output_file), $params{export_svg} ? &Slic3r::GUI::FILE_WILDCARDS->{svg} : &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||
if ($dlg->ShowModal != wxID_OK) {
|
||||
$dlg->Destroy;
|
||||
return;
|
||||
}
|
||||
$output_file = $dlg->GetPath;
|
||||
$qs_last_output_file = $output_file unless $params{export_svg};
|
||||
wxTheApp->{app_config}->update_last_output_dir(dirname($output_file));
|
||||
$dlg->Destroy;
|
||||
}
|
||||
|
||||
# show processbar dialog
|
||||
$progress_dialog = Wx::ProgressDialog->new(L('Slicing…'), L("Processing ").$input_file_basename."…",
|
||||
100, $self, 0);
|
||||
$progress_dialog->Pulse;
|
||||
|
||||
{
|
||||
my @warnings = ();
|
||||
local $SIG{__WARN__} = sub { push @warnings, $_[0] };
|
||||
|
||||
$sprint->output_file($output_file);
|
||||
if ($params{export_svg}) {
|
||||
$sprint->export_svg;
|
||||
} else {
|
||||
$sprint->export_gcode;
|
||||
}
|
||||
$sprint->status_cb(undef);
|
||||
Slic3r::GUI::warning_catcher($self)->($_) for @warnings;
|
||||
}
|
||||
$progress_dialog->Destroy;
|
||||
undef $progress_dialog;
|
||||
|
||||
my $message = $input_file_basename.L(" was successfully sliced.");
|
||||
wxTheApp->notify($message);
|
||||
Wx::MessageDialog->new($self, $message, L('Slicing Done!'),
|
||||
wxOK | wxICON_INFORMATION)->ShowModal;
|
||||
};
|
||||
Slic3r::GUI::catch_error($self, sub { $progress_dialog->Destroy if $progress_dialog });
|
||||
sub slice_to_png {
|
||||
my $self = shift;
|
||||
$self->{plater}->stop_background_process;
|
||||
$self->{plater}->async_apply_config;
|
||||
$appController->print_ctl()->slice_to_png();
|
||||
}
|
||||
|
||||
sub reslice_now {
|
||||
|
|
|
@ -1,498 +0,0 @@
|
|||
# A dialog group object. Used by the Tab, Preferences dialog, ManualControlDialog etc.
|
||||
|
||||
package Slic3r::GUI::OptionsGroup;
|
||||
use Moo;
|
||||
|
||||
use List::Util qw(first);
|
||||
use Wx qw(:combobox :font :misc :sizer :systemsettings :textctrl wxTheApp);
|
||||
use Wx::Event qw(EVT_CHECKBOX EVT_COMBOBOX EVT_SPINCTRL EVT_TEXT EVT_KILL_FOCUS EVT_SLIDER);
|
||||
|
||||
has 'parent' => (is => 'ro', required => 1);
|
||||
has 'title' => (is => 'ro', required => 1);
|
||||
has 'on_change' => (is => 'rw', default => sub { sub {} });
|
||||
has 'staticbox' => (is => 'ro', default => sub { 1 });
|
||||
has 'label_width' => (is => 'rw', default => sub { 180 });
|
||||
has 'extra_column' => (is => 'rw', default => sub { undef });
|
||||
has 'label_font' => (is => 'rw');
|
||||
has 'sidetext_font' => (is => 'rw', default => sub { Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) });
|
||||
has 'sizer' => (is => 'rw');
|
||||
has '_disabled' => (is => 'rw', default => sub { 0 });
|
||||
has '_grid_sizer' => (is => 'rw');
|
||||
has '_options' => (is => 'ro', default => sub { {} });
|
||||
has '_fields' => (is => 'ro', default => sub { {} });
|
||||
|
||||
sub BUILD {
|
||||
my $self = shift;
|
||||
|
||||
if ($self->staticbox) {
|
||||
my $box = Wx::StaticBox->new($self->parent, -1, $self->title);
|
||||
$self->sizer(Wx::StaticBoxSizer->new($box, wxVERTICAL));
|
||||
} else {
|
||||
$self->sizer(Wx::BoxSizer->new(wxVERTICAL));
|
||||
}
|
||||
|
||||
my $num_columns = 1;
|
||||
++$num_columns if $self->label_width != 0;
|
||||
++$num_columns if $self->extra_column;
|
||||
$self->_grid_sizer(Wx::FlexGridSizer->new(0, $num_columns, 0, 0));
|
||||
$self->_grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
|
||||
$self->_grid_sizer->AddGrowableCol($self->label_width != 0);
|
||||
|
||||
# TODO: border size may be related to wxWidgets 2.8.x vs. 2.9.x instead of wxMAC specific
|
||||
$self->sizer->Add($self->_grid_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 5);
|
||||
}
|
||||
|
||||
# this method accepts a Slic3r::GUI::OptionsGroup::Line object
|
||||
sub append_line {
|
||||
my ($self, $line) = @_;
|
||||
|
||||
if ($line->sizer || ($line->widget && $line->full_width)) {
|
||||
# full-width widgets are appended *after* the grid sizer, so after all the non-full-width lines
|
||||
my $sizer = $line->sizer // $line->widget->($self->parent);
|
||||
$self->sizer->Add($sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15);
|
||||
return;
|
||||
}
|
||||
|
||||
my $grid_sizer = $self->_grid_sizer;
|
||||
|
||||
# if we have an extra column, build it
|
||||
if ($self->extra_column) {
|
||||
if (defined (my $item = $self->extra_column->($line))) {
|
||||
$grid_sizer->Add($item, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
} else {
|
||||
# if the callback provides no sizer for the extra cell, put a spacer
|
||||
$grid_sizer->AddSpacer(1);
|
||||
}
|
||||
}
|
||||
|
||||
# build label if we have it
|
||||
my $label;
|
||||
if ($self->label_width != 0) {
|
||||
$label = Wx::StaticText->new($self->parent, -1, $line->label ? $line->label . ":" : "", wxDefaultPosition, [$self->label_width, -1]);
|
||||
$label->SetFont($self->label_font) if $self->label_font;
|
||||
$label->Wrap($self->label_width) ; # needed to avoid Linux/GTK bug
|
||||
$grid_sizer->Add($label, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
$label->SetToolTipString($line->label_tooltip) if $line->label_tooltip;
|
||||
}
|
||||
|
||||
# if we have a widget, add it to the sizer
|
||||
if ($line->widget) {
|
||||
my $widget_sizer = $line->widget->($self->parent);
|
||||
$grid_sizer->Add($widget_sizer, 0, wxEXPAND | wxALL, &Wx::wxMAC ? 0 : 15);
|
||||
return;
|
||||
}
|
||||
|
||||
# if we have a single option with no sidetext just add it directly to the grid sizer
|
||||
my @options = @{$line->get_options};
|
||||
$self->_options->{$_->opt_id} = $_ for @options;
|
||||
if (@options == 1 && !$options[0]->sidetext && !$options[0]->side_widget && !@{$line->get_extra_widgets}) {
|
||||
my $option = $options[0];
|
||||
my $field = $self->_build_field($option);
|
||||
$grid_sizer->Add($field, 0, ($option->full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
# if we're here, we have more than one option or a single option with sidetext
|
||||
# so we need a horizontal sizer to arrange these things
|
||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$grid_sizer->Add($sizer, 0, 0, 0);
|
||||
|
||||
foreach my $i (0..$#options) {
|
||||
my $option = $options[$i];
|
||||
|
||||
# add label if any
|
||||
if ($option->label) {
|
||||
my $field_label = Wx::StaticText->new($self->parent, -1, $option->label . ":", wxDefaultPosition, wxDefaultSize);
|
||||
$field_label->SetFont($self->sidetext_font);
|
||||
$sizer->Add($field_label, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
}
|
||||
|
||||
# add field
|
||||
my $field = $self->_build_field($option);
|
||||
$sizer->Add($field, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
|
||||
# add sidetext if any
|
||||
if ($option->sidetext) {
|
||||
my $sidetext = Wx::StaticText->new($self->parent, -1, $option->sidetext, wxDefaultPosition, wxDefaultSize);
|
||||
$sidetext->SetFont($self->sidetext_font);
|
||||
$sizer->Add($sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
|
||||
}
|
||||
|
||||
# add side widget if any
|
||||
if ($option->side_widget) {
|
||||
$sizer->Add($option->side_widget->($self->parent), 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1);
|
||||
}
|
||||
|
||||
if ($option != $#options) {
|
||||
$sizer->AddSpacer(4);
|
||||
}
|
||||
}
|
||||
|
||||
# add extra sizers if any
|
||||
foreach my $extra_widget (@{$line->get_extra_widgets}) {
|
||||
$sizer->Add($extra_widget->($self->parent), 0, wxLEFT | wxALIGN_CENTER_VERTICAL , 4);
|
||||
}
|
||||
}
|
||||
|
||||
sub create_single_option_line {
|
||||
my ($self, $option) = @_;
|
||||
|
||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
||||
label => $option->label,
|
||||
label_tooltip => $option->tooltip,
|
||||
);
|
||||
$option->label("");
|
||||
$line->append_option($option);
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
sub append_single_option_line {
|
||||
my ($self, $option) = @_;
|
||||
return $self->append_line($self->create_single_option_line($option));
|
||||
}
|
||||
|
||||
sub _build_field {
|
||||
my $self = shift;
|
||||
my ($opt) = @_;
|
||||
|
||||
my $opt_id = $opt->opt_id;
|
||||
my $on_change = sub {
|
||||
#! This function will be called from Field.
|
||||
my ($opt_id, $value) = @_;
|
||||
#! Call OptionGroup._on_change(...)
|
||||
$self->_on_change($opt_id, $value)
|
||||
unless $self->_disabled;
|
||||
};
|
||||
my $on_kill_focus = sub {
|
||||
my ($opt_id) = @_;
|
||||
$self->_on_kill_focus($opt_id);
|
||||
};
|
||||
|
||||
my $type = $opt->{gui_type} || $opt->{type};
|
||||
|
||||
my $field;
|
||||
if ($type eq 'bool') {
|
||||
$field = Slic3r::GUI::OptionsGroup::Field::Checkbox->new(
|
||||
parent => $self->parent,
|
||||
option => $opt,
|
||||
);
|
||||
} elsif ($type eq 'i') {
|
||||
$field = Slic3r::GUI::OptionsGroup::Field::SpinCtrl->new(
|
||||
parent => $self->parent,
|
||||
option => $opt,
|
||||
);
|
||||
} elsif ($type eq 'color') {
|
||||
$field = Slic3r::GUI::OptionsGroup::Field::ColourPicker->new(
|
||||
parent => $self->parent,
|
||||
option => $opt,
|
||||
);
|
||||
} elsif ($type =~ /^(f|s|s@|percent)$/) {
|
||||
$field = Slic3r::GUI::OptionsGroup::Field::TextCtrl->new(
|
||||
parent => $self->parent,
|
||||
option => $opt,
|
||||
);
|
||||
} elsif ($type eq 'select' || $type eq 'select_open') {
|
||||
$field = Slic3r::GUI::OptionsGroup::Field::Choice->new(
|
||||
parent => $self->parent,
|
||||
option => $opt,
|
||||
);
|
||||
} elsif ($type eq 'f_enum_open' || $type eq 'i_enum_open' || $type eq 'i_enum_closed') {
|
||||
$field = Slic3r::GUI::OptionsGroup::Field::NumericChoice->new(
|
||||
parent => $self->parent,
|
||||
option => $opt,
|
||||
);
|
||||
} elsif ($type eq 'point') {
|
||||
$field = Slic3r::GUI::OptionsGroup::Field::Point->new(
|
||||
parent => $self->parent,
|
||||
option => $opt,
|
||||
);
|
||||
} elsif ($type eq 'slider') {
|
||||
$field = Slic3r::GUI::OptionsGroup::Field::Slider->new(
|
||||
parent => $self->parent,
|
||||
option => $opt,
|
||||
);
|
||||
}
|
||||
return undef if !$field;
|
||||
|
||||
#! setting up a function that will be triggered when the field changes
|
||||
#! think of it as $field->on_change = ($on_change)
|
||||
$field->on_change($on_change);
|
||||
$field->on_kill_focus($on_kill_focus);
|
||||
$self->_fields->{$opt_id} = $field;
|
||||
|
||||
return $field->isa('Slic3r::GUI::OptionsGroup::Field::wxWindow')
|
||||
? $field->wxWindow
|
||||
: $field->wxSizer;
|
||||
}
|
||||
|
||||
sub get_option {
|
||||
my ($self, $opt_id) = @_;
|
||||
return undef if !exists $self->_options->{$opt_id};
|
||||
return $self->_options->{$opt_id};
|
||||
}
|
||||
|
||||
sub get_field {
|
||||
my ($self, $opt_id) = @_;
|
||||
return undef if !exists $self->_fields->{$opt_id};
|
||||
return $self->_fields->{$opt_id};
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self, $opt_id) = @_;
|
||||
|
||||
return if !exists $self->_fields->{$opt_id};
|
||||
return $self->_fields->{$opt_id}->get_value;
|
||||
}
|
||||
|
||||
sub set_value {
|
||||
my ($self, $opt_id, $value) = @_;
|
||||
|
||||
return if !exists $self->_fields->{$opt_id};
|
||||
$self->_fields->{$opt_id}->set_value($value);
|
||||
}
|
||||
|
||||
sub _on_change {
|
||||
my ($self, $opt_id, $value) = @_;
|
||||
$self->on_change->($opt_id, $value);
|
||||
}
|
||||
|
||||
sub enable {
|
||||
my ($self) = @_;
|
||||
|
||||
$_->enable for values %{$self->_fields};
|
||||
}
|
||||
|
||||
sub disable {
|
||||
my ($self) = @_;
|
||||
|
||||
$_->disable for values %{$self->_fields};
|
||||
}
|
||||
|
||||
sub _on_kill_focus {
|
||||
my ($self, $opt_id) = @_;
|
||||
# nothing
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Line;
|
||||
use Moo;
|
||||
|
||||
has 'label' => (is => 'rw', default => sub { "" });
|
||||
has 'full_width' => (is => 'rw', default => sub { 0 });
|
||||
has 'label_tooltip' => (is => 'rw', default => sub { "" });
|
||||
has 'sizer' => (is => 'rw');
|
||||
has 'widget' => (is => 'rw');
|
||||
has '_options' => (is => 'ro', default => sub { [] });
|
||||
# Extra UI components after the label and the edit widget of the option.
|
||||
has '_extra_widgets' => (is => 'ro', default => sub { [] });
|
||||
|
||||
# this method accepts a Slic3r::GUI::OptionsGroup::Option object
|
||||
sub append_option {
|
||||
my ($self, $option) = @_;
|
||||
push @{$self->_options}, $option;
|
||||
}
|
||||
|
||||
sub append_widget {
|
||||
my ($self, $widget) = @_;
|
||||
push @{$self->_extra_widgets}, $widget;
|
||||
}
|
||||
|
||||
sub get_options {
|
||||
my ($self) = @_;
|
||||
return [ @{$self->_options} ];
|
||||
}
|
||||
|
||||
sub get_extra_widgets {
|
||||
my ($self) = @_;
|
||||
return [ @{$self->_extra_widgets} ];
|
||||
}
|
||||
|
||||
|
||||
# Configuration of an option.
|
||||
# This very much reflects the content of the C++ ConfigOptionDef class.
|
||||
package Slic3r::GUI::OptionsGroup::Option;
|
||||
use Moo;
|
||||
|
||||
has 'opt_id' => (is => 'rw', required => 1);
|
||||
has 'type' => (is => 'rw', required => 1);
|
||||
has 'default' => (is => 'rw', required => 1);
|
||||
has 'gui_type' => (is => 'rw', default => sub { undef });
|
||||
has 'gui_flags' => (is => 'rw', default => sub { "" });
|
||||
has 'label' => (is => 'rw', default => sub { "" });
|
||||
has 'sidetext' => (is => 'rw', default => sub { "" });
|
||||
has 'tooltip' => (is => 'rw', default => sub { "" });
|
||||
has 'multiline' => (is => 'rw', default => sub { 0 });
|
||||
has 'full_width' => (is => 'rw', default => sub { 0 });
|
||||
has 'width' => (is => 'rw', default => sub { undef });
|
||||
has 'height' => (is => 'rw', default => sub { undef });
|
||||
has 'min' => (is => 'rw', default => sub { undef });
|
||||
has 'max' => (is => 'rw', default => sub { undef });
|
||||
has 'labels' => (is => 'rw', default => sub { [] });
|
||||
has 'values' => (is => 'rw', default => sub { [] });
|
||||
has 'readonly' => (is => 'rw', default => sub { 0 });
|
||||
has 'side_widget' => (is => 'rw', default => sub { undef });
|
||||
|
||||
|
||||
package Slic3r::GUI::ConfigOptionsGroup;
|
||||
use Moo;
|
||||
|
||||
use List::Util qw(first);
|
||||
|
||||
extends 'Slic3r::GUI::OptionsGroup';
|
||||
has 'config' => (is => 'ro', required => 1);
|
||||
has 'full_labels' => (is => 'ro', default => sub { 0 });
|
||||
has '_opt_map' => (is => 'ro', default => sub { {} });
|
||||
|
||||
sub get_option {
|
||||
my ($self, $opt_key, $opt_index) = @_;
|
||||
|
||||
$opt_index //= -1;
|
||||
|
||||
if (!$self->config->has($opt_key)) {
|
||||
die "No $opt_key in ConfigOptionsGroup config";
|
||||
}
|
||||
|
||||
my $opt_id = ($opt_index == -1 ? $opt_key : "${opt_key}#${opt_index}");
|
||||
$self->_opt_map->{$opt_id} = [ $opt_key, $opt_index ];
|
||||
|
||||
# Slic3r::Config::Options is a C++ Slic3r::PrintConfigDef exported as a Perl hash of hashes.
|
||||
# The C++ counterpart is a constant singleton.
|
||||
my $optdef = $Slic3r::Config::Options->{$opt_key}; # we should access this from $self->config
|
||||
my $default_value = $self->_get_config_value($opt_key, $opt_index, $optdef->{gui_flags} =~ /\bserialized\b/);
|
||||
|
||||
return Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => $opt_id,
|
||||
type => $optdef->{type},
|
||||
default => $default_value,
|
||||
gui_type => $optdef->{gui_type},
|
||||
gui_flags => $optdef->{gui_flags},
|
||||
label => ($self->full_labels && defined $optdef->{full_label}) ? $optdef->{full_label} : $optdef->{label},
|
||||
sidetext => $optdef->{sidetext},
|
||||
# calling serialize() ensures we get a stringified value
|
||||
tooltip => $optdef->{tooltip} . " (default: " . $self->config->serialize($opt_key) . ")",
|
||||
multiline => $optdef->{multiline},
|
||||
width => $optdef->{width},
|
||||
min => $optdef->{min},
|
||||
max => $optdef->{max},
|
||||
labels => $optdef->{labels},
|
||||
values => $optdef->{values},
|
||||
readonly => $optdef->{readonly},
|
||||
);
|
||||
}
|
||||
|
||||
sub create_single_option_line {
|
||||
my ($self, $opt_key, $opt_index) = @_;
|
||||
|
||||
my $option;
|
||||
if (ref($opt_key)) {
|
||||
$option = $opt_key;
|
||||
} else {
|
||||
$option = $self->get_option($opt_key, $opt_index);
|
||||
}
|
||||
return $self->SUPER::create_single_option_line($option);
|
||||
}
|
||||
|
||||
sub append_single_option_line {
|
||||
my ($self, $option, $opt_index) = @_;
|
||||
return $self->append_line($self->create_single_option_line($option, $opt_index));
|
||||
}
|
||||
|
||||
# Initialize UI components with the config values.
|
||||
sub reload_config {
|
||||
my ($self) = @_;
|
||||
|
||||
foreach my $opt_id (keys %{ $self->_opt_map }) {
|
||||
my ($opt_key, $opt_index) = @{ $self->_opt_map->{$opt_id} };
|
||||
my $option = $self->_options->{$opt_id};
|
||||
$self->set_value($opt_id, $self->_get_config_value($opt_key, $opt_index, $option->gui_flags =~ /\bserialized\b/));
|
||||
}
|
||||
}
|
||||
|
||||
sub get_fieldc {
|
||||
my ($self, $opt_key, $opt_index) = @_;
|
||||
|
||||
$opt_index //= -1;
|
||||
my $opt_id = first { $self->_opt_map->{$_}[0] eq $opt_key && $self->_opt_map->{$_}[1] == $opt_index }
|
||||
keys %{$self->_opt_map};
|
||||
return defined($opt_id) ? $self->get_field($opt_id) : undef;
|
||||
}
|
||||
|
||||
sub _get_config_value {
|
||||
my ($self, $opt_key, $opt_index, $deserialize) = @_;
|
||||
|
||||
if ($deserialize) {
|
||||
# Want to edit a vector value (currently only multi-strings) in a single edit box.
|
||||
# Aggregate the strings the old way.
|
||||
# Currently used for the post_process config value only.
|
||||
die "Can't deserialize option indexed value" if $opt_index != -1;
|
||||
return join(';', @{$self->config->get($opt_key)});
|
||||
} else {
|
||||
return $opt_index == -1
|
||||
? $self->config->get($opt_key)
|
||||
: $self->config->get_at($opt_key, $opt_index);
|
||||
}
|
||||
}
|
||||
|
||||
sub _on_change {
|
||||
my ($self, $opt_id, $value) = @_;
|
||||
|
||||
if (exists $self->_opt_map->{$opt_id}) {
|
||||
my ($opt_key, $opt_index) = @{ $self->_opt_map->{$opt_id} };
|
||||
my $option = $self->_options->{$opt_id};
|
||||
|
||||
# get value
|
||||
my $field_value = $self->get_value($opt_id);
|
||||
if ($option->gui_flags =~ /\bserialized\b/) {
|
||||
die "Can't set serialized option indexed value" if $opt_index != -1;
|
||||
# Split a string to multiple strings by a semi-colon. This is the old way of storing multi-string values.
|
||||
# Currently used for the post_process config value only.
|
||||
my @values = split /;/, $field_value;
|
||||
$self->config->set($opt_key, \@values);
|
||||
} else {
|
||||
if ($opt_index == -1) {
|
||||
$self->config->set($opt_key, $field_value);
|
||||
} else {
|
||||
my $value = $self->config->get($opt_key);
|
||||
$value->[$opt_index] = $field_value;
|
||||
$self->config->set($opt_key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$self->SUPER::_on_change($opt_id, $value);
|
||||
}
|
||||
|
||||
sub _on_kill_focus {
|
||||
my ($self, $opt_id) = @_;
|
||||
|
||||
# when a field loses focus, reapply the config value to it
|
||||
# (thus discarding any invalid input and reverting to the last
|
||||
# accepted value)
|
||||
$self->reload_config;
|
||||
}
|
||||
|
||||
# Static text shown among the options.
|
||||
# Currently used for the filament cooling legend only.
|
||||
package Slic3r::GUI::OptionsGroup::StaticText;
|
||||
use Wx qw(:misc :systemsettings);
|
||||
use base 'Wx::StaticText';
|
||||
|
||||
sub new {
|
||||
my ($class, $parent) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent, -1, "", wxDefaultPosition, wxDefaultSize);
|
||||
my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
$self->SetFont($font);
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub SetText {
|
||||
my ($self, $value) = @_;
|
||||
|
||||
$self->SetLabel($value);
|
||||
$self->Wrap(400);
|
||||
$self->GetParent->Layout;
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,605 +0,0 @@
|
|||
# An input field class prototype.
|
||||
package Slic3r::GUI::OptionsGroup::Field;
|
||||
use Moo;
|
||||
|
||||
# This is a base class for option fields.
|
||||
|
||||
has 'parent' => (is => 'ro', required => 1);
|
||||
# Slic3r::GUI::OptionsGroup::Option
|
||||
has 'option' => (is => 'ro', required => 1);
|
||||
# On change callback
|
||||
has 'on_change' => (is => 'rw', default => sub { sub {} });
|
||||
has 'on_kill_focus' => (is => 'rw', default => sub { sub {} });
|
||||
# If set, the callback $self->on_change is not called.
|
||||
# This is used to avoid recursive invocation of the field change/update by wxWidgets.
|
||||
has 'disable_change_event' => (is => 'rw', default => sub { 0 });
|
||||
|
||||
# This method should not fire the on_change event
|
||||
sub set_value {
|
||||
my ($self, $value) = @_;
|
||||
die "Method not implemented";
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
die "Method not implemented";
|
||||
}
|
||||
|
||||
sub set_tooltip {
|
||||
my ($self, $tooltip) = @_;
|
||||
|
||||
$self->SetToolTipString($tooltip)
|
||||
if $tooltip && $self->can('SetToolTipString');
|
||||
}
|
||||
|
||||
sub toggle {
|
||||
my ($self, $enable) = @_;
|
||||
$enable ? $self->enable : $self->disable;
|
||||
}
|
||||
|
||||
sub _on_change {
|
||||
my ($self, $opt_id) = @_;
|
||||
|
||||
$self->on_change->($opt_id, $self->get_value)
|
||||
unless $self->disable_change_event;
|
||||
}
|
||||
|
||||
sub _on_kill_focus {
|
||||
my ($self, $opt_id, $s, $event) = @_;
|
||||
|
||||
# Without this, there will be nasty focus bugs on Windows.
|
||||
# Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all
|
||||
# non-command events to allow the default handling to take place."
|
||||
$event->Skip(1);
|
||||
|
||||
$self->on_kill_focus->($opt_id);
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::wxWindow;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field';
|
||||
|
||||
has 'wxWindow' => (is => 'rw', trigger => 1); # wxWindow object
|
||||
|
||||
sub _default_size {
|
||||
my ($self) = @_;
|
||||
|
||||
# default width on Windows is too large
|
||||
return Wx::Size->new($self->option->width || 60, $self->option->height || -1);
|
||||
}
|
||||
|
||||
sub _trigger_wxWindow {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->wxWindow->SetToolTipString($self->option->tooltip)
|
||||
if $self->option->tooltip && $self->wxWindow->can('SetToolTipString');
|
||||
}
|
||||
|
||||
sub set_value {
|
||||
my ($self, $value) = @_;
|
||||
|
||||
$self->disable_change_event(1);
|
||||
$self->wxWindow->SetValue($value);
|
||||
$self->disable_change_event(0);
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
return $self->wxWindow->GetValue;
|
||||
}
|
||||
|
||||
sub enable {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->wxWindow->Enable;
|
||||
$self->wxWindow->Refresh;
|
||||
}
|
||||
|
||||
sub disable {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->wxWindow->Disable;
|
||||
$self->wxWindow->Refresh;
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::Checkbox;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow';
|
||||
|
||||
use Wx qw(:misc);
|
||||
use Wx::Event qw(EVT_CHECKBOX);
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
my $field = Wx::CheckBox->new($self->parent, -1, "");
|
||||
$self->wxWindow($field);
|
||||
$field->SetValue($self->option->default);
|
||||
$field->Disable if $self->option->readonly;
|
||||
|
||||
EVT_CHECKBOX($self->parent, $field, sub {
|
||||
$self->_on_change($self->option->opt_id);
|
||||
});
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
return $self->wxWindow->GetValue ? 1 : 0;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::SpinCtrl;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow';
|
||||
|
||||
use Wx qw(:misc);
|
||||
use Wx::Event qw(EVT_SPINCTRL EVT_TEXT EVT_KILL_FOCUS);
|
||||
|
||||
has 'tmp_value' => (is => 'rw');
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
my $field = Wx::SpinCtrl->new($self->parent, -1, $self->option->default, wxDefaultPosition, $self->_default_size,
|
||||
0, $self->option->min || 0, $self->option->max || 2147483647, $self->option->default);
|
||||
$self->wxWindow($field);
|
||||
|
||||
EVT_SPINCTRL($self->parent, $field, sub {
|
||||
$self->tmp_value(undef);
|
||||
$self->_on_change($self->option->opt_id);
|
||||
});
|
||||
EVT_TEXT($self->parent, $field, sub {
|
||||
my ($s, $event) = @_;
|
||||
|
||||
# On OSX/Cocoa, wxSpinCtrl::GetValue() doesn't return the new value
|
||||
# when it was changed from the text control, so the on_change callback
|
||||
# gets the old one, and on_kill_focus resets the control to the old value.
|
||||
# As a workaround, we get the new value from $event->GetString and store
|
||||
# here temporarily so that we can return it from $self->get_value
|
||||
$self->tmp_value($event->GetString) if $event->GetString =~ /^\d+$/;
|
||||
$self->_on_change($self->option->opt_id);
|
||||
# We don't reset tmp_value here because _on_change might put callbacks
|
||||
# in the CallAfter queue, and we want the tmp value to be available from
|
||||
# them as well.
|
||||
});
|
||||
EVT_KILL_FOCUS($field, sub {
|
||||
$self->tmp_value(undef);
|
||||
$self->_on_kill_focus($self->option->opt_id, @_);
|
||||
});
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
return $self->tmp_value // $self->wxWindow->GetValue;
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::TextCtrl;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow';
|
||||
|
||||
use Wx qw(:misc :textctrl);
|
||||
use Wx::Event qw(EVT_TEXT EVT_KILL_FOCUS);
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
my $style = 0;
|
||||
$style = wxTE_MULTILINE if $self->option->multiline;
|
||||
my $field = Wx::TextCtrl->new($self->parent, -1, $self->option->default, wxDefaultPosition,
|
||||
$self->_default_size, $style);
|
||||
$self->wxWindow($field);
|
||||
|
||||
# TODO: test loading a config that has empty string for multi-value options like 'wipe'
|
||||
|
||||
EVT_TEXT($self->parent, $field, sub {
|
||||
$self->_on_change($self->option->opt_id);
|
||||
});
|
||||
EVT_KILL_FOCUS($field, sub {
|
||||
$self->_on_kill_focus($self->option->opt_id, @_);
|
||||
});
|
||||
}
|
||||
|
||||
sub enable {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->wxWindow->Enable;
|
||||
$self->wxWindow->SetEditable(1);
|
||||
}
|
||||
|
||||
sub disable {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->wxWindow->Disable;
|
||||
$self->wxWindow->SetEditable(0);
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::Choice;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow';
|
||||
|
||||
use List::Util qw(first);
|
||||
use Wx qw(:misc :combobox);
|
||||
use Wx::Event qw(EVT_COMBOBOX EVT_TEXT);
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
my $style = 0;
|
||||
$style |= wxCB_READONLY if defined $self->option->gui_type && $self->option->gui_type ne 'select_open';
|
||||
my $field = Wx::ComboBox->new($self->parent, -1, "", wxDefaultPosition, $self->_default_size,
|
||||
$self->option->labels || $self->option->values || [], $style);
|
||||
$self->wxWindow($field);
|
||||
|
||||
$self->set_value($self->option->default);
|
||||
|
||||
EVT_COMBOBOX($self->parent, $field, sub {
|
||||
$self->_on_change($self->option->opt_id);
|
||||
});
|
||||
EVT_TEXT($self->parent, $field, sub {
|
||||
$self->_on_change($self->option->opt_id);
|
||||
});
|
||||
}
|
||||
|
||||
sub set_value {
|
||||
my ($self, $value) = @_;
|
||||
|
||||
$self->disable_change_event(1);
|
||||
|
||||
my $idx;
|
||||
if ($self->option->values) {
|
||||
$idx = first { $self->option->values->[$_] eq $value } 0..$#{$self->option->values};
|
||||
# if value is not among indexes values we use SetValue()
|
||||
}
|
||||
|
||||
if (defined $idx) {
|
||||
$self->wxWindow->SetSelection($idx);
|
||||
} else {
|
||||
$self->wxWindow->SetValue($value);
|
||||
}
|
||||
|
||||
$self->disable_change_event(0);
|
||||
}
|
||||
|
||||
sub set_values {
|
||||
my ($self, $values) = @_;
|
||||
|
||||
$self->disable_change_event(1);
|
||||
|
||||
# it looks that Clear() also clears the text field in recent wxWidgets versions,
|
||||
# but we want to preserve it
|
||||
my $ww = $self->wxWindow;
|
||||
my $value = $ww->GetValue;
|
||||
$ww->Clear;
|
||||
$ww->Append($_) for @$values;
|
||||
$ww->SetValue($value);
|
||||
|
||||
$self->disable_change_event(0);
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
|
||||
if ($self->option->values) {
|
||||
my $idx = $self->wxWindow->GetSelection;
|
||||
if ($idx != &Wx::wxNOT_FOUND) {
|
||||
return $self->option->values->[$idx];
|
||||
}
|
||||
}
|
||||
return $self->wxWindow->GetValue;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::NumericChoice;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow';
|
||||
|
||||
use List::Util qw(first);
|
||||
use Wx qw(wxTheApp :misc :combobox);
|
||||
use Wx::Event qw(EVT_COMBOBOX EVT_TEXT);
|
||||
|
||||
# if option has no 'values', indices are values
|
||||
# if option has no 'labels', values are labels
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
my $field = Wx::ComboBox->new($self->parent, -1, $self->option->default, wxDefaultPosition, $self->_default_size,
|
||||
$self->option->labels || $self->option->values);
|
||||
$self->wxWindow($field);
|
||||
|
||||
$self->set_value($self->option->default);
|
||||
|
||||
EVT_COMBOBOX($self->parent, $field, sub {
|
||||
my $disable_change_event = $self->disable_change_event;
|
||||
$self->disable_change_event(1);
|
||||
|
||||
my $idx = $field->GetSelection; # get index of selected value
|
||||
my $label;
|
||||
|
||||
if ($self->option->labels && $idx <= $#{$self->option->labels}) {
|
||||
$label = $self->option->labels->[$idx];
|
||||
} elsif ($self->option->values && $idx <= $#{$self->option->values}) {
|
||||
$label = $self->option->values->[$idx];
|
||||
} else {
|
||||
$label = $idx;
|
||||
}
|
||||
|
||||
# The MSW implementation of wxComboBox will leave the field blank if we call
|
||||
# SetValue() in the EVT_COMBOBOX event handler, so we postpone the call.
|
||||
wxTheApp->CallAfter(sub {
|
||||
my $dce = $self->disable_change_event;
|
||||
$self->disable_change_event(1);
|
||||
|
||||
# ChangeValue() is not exported in wxPerl
|
||||
$field->SetValue($label);
|
||||
|
||||
$self->disable_change_event($dce);
|
||||
});
|
||||
|
||||
$self->disable_change_event($disable_change_event);
|
||||
$self->_on_change($self->option->opt_id);
|
||||
});
|
||||
EVT_TEXT($self->parent, $field, sub {
|
||||
$self->_on_change($self->option->opt_id);
|
||||
});
|
||||
}
|
||||
|
||||
sub set_value {
|
||||
my ($self, $value) = @_;
|
||||
|
||||
$self->disable_change_event(1);
|
||||
|
||||
my $field = $self->wxWindow;
|
||||
if ($self->option->gui_flags =~ /\bshow_value\b/) {
|
||||
$field->SetValue($value);
|
||||
} else {
|
||||
if ($self->option->values) {
|
||||
# check whether we have a value index
|
||||
my $value_idx = first { $self->option->values->[$_] eq $value } 0..$#{$self->option->values};
|
||||
if (defined $value_idx) {
|
||||
$field->SetSelection($value_idx);
|
||||
$self->disable_change_event(0);
|
||||
return;
|
||||
}
|
||||
} elsif ($self->option->labels && $value <= $#{$self->option->labels}) {
|
||||
# if we have no values, we expect value to be an index
|
||||
$field->SetValue($self->option->labels->[$value]);
|
||||
$self->disable_change_event(0);
|
||||
return;
|
||||
}
|
||||
$field->SetValue($value);
|
||||
}
|
||||
|
||||
$self->disable_change_event(0);
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
|
||||
my $label = $self->wxWindow->GetValue;
|
||||
if ($self->option->labels) {
|
||||
my $value_idx = first { $self->option->labels->[$_] eq $label } 0..$#{$self->option->labels};
|
||||
if (defined $value_idx) {
|
||||
if ($self->option->values) {
|
||||
return $self->option->values->[$value_idx];
|
||||
}
|
||||
return $value_idx;
|
||||
}
|
||||
}
|
||||
return $label;
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::ColourPicker;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field::wxWindow';
|
||||
|
||||
use Wx qw(:misc :colour);
|
||||
use Wx::Event qw(EVT_COLOURPICKER_CHANGED);
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
my $field = Wx::ColourPickerCtrl->new($self->parent, -1,
|
||||
$self->_string_to_colour($self->option->default), wxDefaultPosition,
|
||||
$self->_default_size);
|
||||
$self->wxWindow($field);
|
||||
|
||||
EVT_COLOURPICKER_CHANGED($self->parent, $field, sub {
|
||||
$self->_on_change($self->option->opt_id);
|
||||
});
|
||||
}
|
||||
|
||||
sub set_value {
|
||||
my ($self, $value) = @_;
|
||||
|
||||
$self->disable_change_event(1);
|
||||
$self->wxWindow->SetColour($self->_string_to_colour($value));
|
||||
$self->disable_change_event(0);
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
return $self->wxWindow->GetColour->GetAsString(wxC2S_HTML_SYNTAX);
|
||||
}
|
||||
|
||||
sub _string_to_colour {
|
||||
my ($self, $string) = @_;
|
||||
|
||||
$string =~ s/^#//;
|
||||
# If the color is in an invalid format, set it to white.
|
||||
$string = 'FFFFFF' if ($string !~ m/^[[:xdigit:]]{6}/);
|
||||
return Wx::Colour->new(unpack 'C*', pack 'H*', $string);
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::wxSizer;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field';
|
||||
|
||||
has 'wxSizer' => (is => 'rw'); # wxSizer object
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::Point;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field::wxSizer';
|
||||
|
||||
has 'x_textctrl' => (is => 'rw');
|
||||
has 'y_textctrl' => (is => 'rw');
|
||||
|
||||
use Slic3r::Geometry qw(X Y);
|
||||
use Wx qw(:misc :sizer);
|
||||
use Wx::Event qw(EVT_TEXT);
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$self->wxSizer($sizer);
|
||||
|
||||
my $field_size = Wx::Size->new(40, -1);
|
||||
|
||||
$self->x_textctrl(Wx::TextCtrl->new($self->parent, -1, $self->option->default->[X], wxDefaultPosition, $field_size));
|
||||
$self->y_textctrl(Wx::TextCtrl->new($self->parent, -1, $self->option->default->[Y], wxDefaultPosition, $field_size));
|
||||
|
||||
my @items = (
|
||||
Wx::StaticText->new($self->parent, -1, "x:"),
|
||||
$self->x_textctrl,
|
||||
Wx::StaticText->new($self->parent, -1, " y:"),
|
||||
$self->y_textctrl,
|
||||
);
|
||||
$sizer->Add($_, 0, wxALIGN_CENTER_VERTICAL, 0) for @items;
|
||||
|
||||
if ($self->option->tooltip) {
|
||||
foreach my $item (@items) {
|
||||
$item->SetToolTipString($self->option->tooltip)
|
||||
if $item->can('SetToolTipString');
|
||||
}
|
||||
}
|
||||
|
||||
EVT_TEXT($self->parent, $_, sub {
|
||||
$self->_on_change($self->option->opt_id);
|
||||
}) for $self->x_textctrl, $self->y_textctrl;
|
||||
}
|
||||
|
||||
sub set_value {
|
||||
my ($self, $value) = @_;
|
||||
|
||||
$self->disable_change_event(1);
|
||||
$self->x_textctrl->SetValue($value->[X]);
|
||||
$self->y_textctrl->SetValue($value->[Y]);
|
||||
$self->disable_change_event(0);
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
|
||||
return [
|
||||
$self->x_textctrl->GetValue,
|
||||
$self->y_textctrl->GetValue,
|
||||
];
|
||||
}
|
||||
|
||||
sub enable {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->x_textctrl->Enable;
|
||||
$self->y_textctrl->Enable;
|
||||
}
|
||||
|
||||
sub disable {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->x_textctrl->Disable;
|
||||
$self->y_textctrl->Disable;
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::OptionsGroup::Field::Slider;
|
||||
use Moo;
|
||||
extends 'Slic3r::GUI::OptionsGroup::Field::wxSizer';
|
||||
|
||||
has 'scale' => (is => 'rw', default => sub { 10 });
|
||||
has 'slider' => (is => 'rw');
|
||||
has 'textctrl' => (is => 'rw');
|
||||
|
||||
use Wx qw(:misc :sizer);
|
||||
use Wx::Event qw(EVT_SLIDER EVT_TEXT EVT_KILL_FOCUS);
|
||||
|
||||
sub BUILD {
|
||||
my ($self) = @_;
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$self->wxSizer($sizer);
|
||||
|
||||
my $slider = Wx::Slider->new(
|
||||
$self->parent, -1,
|
||||
($self->option->default // $self->option->min) * $self->scale,
|
||||
($self->option->min // 0) * $self->scale,
|
||||
($self->option->max // 100) * $self->scale,
|
||||
wxDefaultPosition,
|
||||
[ $self->option->width // -1, $self->option->height // -1 ],
|
||||
);
|
||||
$self->slider($slider);
|
||||
|
||||
my $textctrl = Wx::TextCtrl->new($self->parent, -1, $slider->GetValue/$self->scale,
|
||||
wxDefaultPosition, [50,-1]);
|
||||
$self->textctrl($textctrl);
|
||||
|
||||
$sizer->Add($slider, 1, wxALIGN_CENTER_VERTICAL, 0);
|
||||
$sizer->Add($textctrl, 0, wxALIGN_CENTER_VERTICAL, 0);
|
||||
|
||||
EVT_SLIDER($self->parent, $slider, sub {
|
||||
if (! $self->disable_change_event) {
|
||||
# wxTextCtrl::SetLabel() does not work on Linux, use wxTextCtrl::SetValue() instead
|
||||
$self->textctrl->SetValue($self->get_value);
|
||||
$self->_on_change($self->option->opt_id);
|
||||
}
|
||||
});
|
||||
EVT_TEXT($self->parent, $textctrl, sub {
|
||||
my $value = $textctrl->GetValue;
|
||||
if ($value =~ /^-?\d+(\.\d*)?$/) {
|
||||
$self->disable_change_event(1);
|
||||
$self->slider->SetValue($value*$self->scale);
|
||||
$self->disable_change_event(0);
|
||||
$self->_on_change($self->option->opt_id);
|
||||
}
|
||||
});
|
||||
EVT_KILL_FOCUS($textctrl, sub {
|
||||
$self->_on_kill_focus($self->option->opt_id, @_);
|
||||
});
|
||||
}
|
||||
|
||||
sub set_value {
|
||||
my ($self, $value) = @_;
|
||||
|
||||
$self->disable_change_event(1);
|
||||
$self->slider->SetValue($value*$self->scale);
|
||||
$self->textctrl->SetLabel($self->get_value);
|
||||
$self->disable_change_event(0);
|
||||
}
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
return $self->slider->GetValue/$self->scale;
|
||||
}
|
||||
|
||||
sub enable {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->slider->Enable;
|
||||
$self->textctrl->Enable;
|
||||
$self->textctrl->SetEditable(1);
|
||||
}
|
||||
|
||||
sub disable {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->slider->Disable;
|
||||
$self->textctrl->Disable;
|
||||
$self->textctrl->SetEditable(0);
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,372 +0,0 @@
|
|||
# 2D preview on the platter.
|
||||
# 3D objects are visualized by their convex hulls.
|
||||
|
||||
package Slic3r::GUI::Plater::2D;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use List::Util qw(min max first);
|
||||
use Slic3r::Geometry qw(X Y scale unscale convex_hull);
|
||||
use Slic3r::Geometry::Clipper qw(offset JT_ROUND intersection_pl);
|
||||
use Wx qw(wxTheApp :misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL);
|
||||
use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE);
|
||||
use base 'Wx::Panel';
|
||||
|
||||
use Wx::Locale gettext => 'L';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $size, $objects, $model, $config) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, $size, wxTAB_TRAVERSAL);
|
||||
# This has only effect on MacOS. On Windows and Linux/GTK, the background is painted by $self->repaint().
|
||||
$self->SetBackgroundColour(Wx::wxWHITE);
|
||||
|
||||
$self->{objects} = $objects;
|
||||
$self->{model} = $model;
|
||||
$self->{config} = $config;
|
||||
$self->{on_select_object} = sub {};
|
||||
$self->{on_double_click} = sub {};
|
||||
$self->{on_right_click} = sub {};
|
||||
$self->{on_instances_moved} = sub {};
|
||||
|
||||
$self->{objects_brush} = Wx::Brush->new(Wx::Colour->new(210,210,210), wxSOLID);
|
||||
$self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), wxSOLID);
|
||||
$self->{dragged_brush} = Wx::Brush->new(Wx::Colour->new(128,128,255), wxSOLID);
|
||||
$self->{transparent_brush} = Wx::Brush->new(Wx::Colour->new(0,0,0), wxTRANSPARENT);
|
||||
$self->{grid_pen} = Wx::Pen->new(Wx::Colour->new(230,230,230), 1, wxSOLID);
|
||||
$self->{print_center_pen} = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID);
|
||||
$self->{clearance_pen} = Wx::Pen->new(Wx::Colour->new(0,0,200), 1, wxSOLID);
|
||||
$self->{skirt_pen} = Wx::Pen->new(Wx::Colour->new(150,150,150), 1, wxSOLID);
|
||||
|
||||
$self->{user_drawn_background} = $^O ne 'darwin';
|
||||
|
||||
EVT_PAINT($self, \&repaint);
|
||||
EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background};
|
||||
EVT_MOUSE_EVENTS($self, \&mouse_event);
|
||||
EVT_SIZE($self, sub {
|
||||
$self->update_bed_size;
|
||||
$self->Refresh;
|
||||
});
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub on_select_object {
|
||||
my ($self, $cb) = @_;
|
||||
$self->{on_select_object} = $cb;
|
||||
}
|
||||
|
||||
sub on_double_click {
|
||||
my ($self, $cb) = @_;
|
||||
$self->{on_double_click} = $cb;
|
||||
}
|
||||
|
||||
sub on_right_click {
|
||||
my ($self, $cb) = @_;
|
||||
$self->{on_right_click} = $cb;
|
||||
}
|
||||
|
||||
sub on_instances_moved {
|
||||
my ($self, $cb) = @_;
|
||||
$self->{on_instances_moved} = $cb;
|
||||
}
|
||||
|
||||
sub repaint {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
my $dc = Wx::AutoBufferedPaintDC->new($self);
|
||||
my $size = $self->GetSize;
|
||||
my @size = ($size->GetWidth, $size->GetHeight);
|
||||
|
||||
if ($self->{user_drawn_background}) {
|
||||
# On all systems the AutoBufferedPaintDC() achieves double buffering.
|
||||
# On MacOS the background is erased, on Windows the background is not erased
|
||||
# and on Linux/GTK the background is erased to gray color.
|
||||
# Fill DC with the background on Windows & Linux/GTK.
|
||||
my $brush_background = Wx::Brush->new(Wx::wxWHITE, wxSOLID);
|
||||
$dc->SetPen(wxWHITE_PEN);
|
||||
$dc->SetBrush($brush_background);
|
||||
my $rect = $self->GetUpdateRegion()->GetBox();
|
||||
$dc->DrawRectangle($rect->GetLeft(), $rect->GetTop(), $rect->GetWidth(), $rect->GetHeight());
|
||||
}
|
||||
|
||||
# draw grid
|
||||
$dc->SetPen($self->{grid_pen});
|
||||
$dc->DrawLine(map @$_, @$_) for @{$self->{grid}};
|
||||
|
||||
# draw bed
|
||||
{
|
||||
$dc->SetPen($self->{print_center_pen});
|
||||
$dc->SetBrush($self->{transparent_brush});
|
||||
$dc->DrawPolygon($self->scaled_points_to_pixel($self->{bed_polygon}, 1), 0, 0);
|
||||
}
|
||||
|
||||
# draw print center
|
||||
if (@{$self->{objects}} && wxTheApp->{app_config}->get("autocenter")) {
|
||||
my $center = $self->unscaled_point_to_pixel($self->{print_center});
|
||||
$dc->SetPen($self->{print_center_pen});
|
||||
$dc->DrawLine($center->[X], 0, $center->[X], $size[Y]);
|
||||
$dc->DrawLine(0, $center->[Y], $size[X], $center->[Y]);
|
||||
$dc->SetTextForeground(Wx::Colour->new(0,0,0));
|
||||
$dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL));
|
||||
$dc->DrawLabel("X = " . sprintf('%.0f', $self->{print_center}->[X]), Wx::Rect->new(0, 0, $center->[X]*2, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_BOTTOM);
|
||||
$dc->DrawRotatedText("Y = " . sprintf('%.0f', $self->{print_center}->[Y]), 0, $center->[Y]+15, 90);
|
||||
}
|
||||
|
||||
# draw frame
|
||||
if (0) {
|
||||
$dc->SetPen(wxBLACK_PEN);
|
||||
$dc->SetBrush($self->{transparent_brush});
|
||||
$dc->DrawRectangle(0, 0, @size);
|
||||
}
|
||||
|
||||
# draw text if plate is empty
|
||||
if (!@{$self->{objects}}) {
|
||||
$dc->SetTextForeground(Wx::Colour->new(150,50,50));
|
||||
$dc->SetFont(Wx::Font->new(14, wxDEFAULT, wxNORMAL, wxNORMAL));
|
||||
$dc->DrawLabel(
|
||||
join('-', +(localtime)[3,4]) eq '13-8'
|
||||
? L('What do you want to print today? ™') # Sept. 13, 2006. The first part ever printed by a RepRap to make another RepRap.
|
||||
: L('Drag your objects here'),
|
||||
Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL);
|
||||
}
|
||||
|
||||
# draw thumbnails
|
||||
$dc->SetPen(wxBLACK_PEN);
|
||||
$self->clean_instance_thumbnails;
|
||||
for my $obj_idx (0 .. $#{$self->{objects}}) {
|
||||
my $object = $self->{objects}[$obj_idx];
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
next unless defined $object->thumbnail;
|
||||
for my $instance_idx (0 .. $#{$model_object->instances}) {
|
||||
my $instance = $model_object->instances->[$instance_idx];
|
||||
next if !defined $object->transformed_thumbnail;
|
||||
|
||||
my $thumbnail = $object->transformed_thumbnail->clone; # in scaled model coordinates
|
||||
$thumbnail->translate(map scale($_), @{$instance->offset});
|
||||
|
||||
$object->instance_thumbnails->[$instance_idx] = $thumbnail;
|
||||
|
||||
if (defined $self->{drag_object} && $self->{drag_object}[0] == $obj_idx && $self->{drag_object}[1] == $instance_idx) {
|
||||
$dc->SetBrush($self->{dragged_brush});
|
||||
} elsif ($object->selected) {
|
||||
$dc->SetBrush($self->{selected_brush});
|
||||
} else {
|
||||
$dc->SetBrush($self->{objects_brush});
|
||||
}
|
||||
foreach my $expolygon (@$thumbnail) {
|
||||
foreach my $points (@{$expolygon->pp}) {
|
||||
$dc->DrawPolygon($self->scaled_points_to_pixel($points, 1), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (0) {
|
||||
# draw bounding box for debugging purposes
|
||||
my $bb = $model_object->instance_bounding_box($instance_idx);
|
||||
$bb->scale($self->{scaling_factor});
|
||||
# no need to translate by instance offset because instance_bounding_box() does that
|
||||
my $points = $bb->polygon->pp;
|
||||
$dc->SetPen($self->{clearance_pen});
|
||||
$dc->SetBrush($self->{transparent_brush});
|
||||
$dc->DrawPolygon($self->_y($points), 0, 0);
|
||||
}
|
||||
|
||||
# if sequential printing is enabled and we have more than one object, draw clearance area
|
||||
if ($self->{config}->complete_objects && (map @{$_->instances}, @{$self->{model}->objects}) > 1) {
|
||||
my ($clearance) = @{offset([$thumbnail->convex_hull], (scale($self->{config}->extruder_clearance_radius) / 2), JT_ROUND, scale(0.1))};
|
||||
$dc->SetPen($self->{clearance_pen});
|
||||
$dc->SetBrush($self->{transparent_brush});
|
||||
$dc->DrawPolygon($self->scaled_points_to_pixel($clearance, 1), 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# draw skirt
|
||||
if (@{$self->{objects}} && $self->{config}->skirts) {
|
||||
my @points = map @{$_->contour}, map @$_, map @{$_->instance_thumbnails}, @{$self->{objects}};
|
||||
if (@points >= 3) {
|
||||
my ($convex_hull) = @{offset([convex_hull(\@points)], scale max($self->{config}->brim_width + $self->{config}->skirt_distance), JT_ROUND, scale(0.1))};
|
||||
$dc->SetPen($self->{skirt_pen});
|
||||
$dc->SetBrush($self->{transparent_brush});
|
||||
$dc->DrawPolygon($self->scaled_points_to_pixel($convex_hull, 1), 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
$event->Skip;
|
||||
}
|
||||
|
||||
sub mouse_event {
|
||||
my ($self, $event) = @_;
|
||||
my $pos = $event->GetPosition;
|
||||
my $point = $self->point_to_model_units([ $pos->x, $pos->y ]); #]]
|
||||
if ($event->ButtonDown) {
|
||||
$self->{on_select_object}->(undef);
|
||||
# traverse objects and instances in reverse order, so that if they're overlapping
|
||||
# we get the one that gets drawn last, thus on top (as user expects that to move)
|
||||
OBJECTS: for my $obj_idx (reverse 0 .. $#{$self->{objects}}) {
|
||||
my $object = $self->{objects}->[$obj_idx];
|
||||
for my $instance_idx (reverse 0 .. $#{ $object->instance_thumbnails }) {
|
||||
my $thumbnail = $object->instance_thumbnails->[$instance_idx];
|
||||
if (defined first { $_->contour->contains_point($point) } @$thumbnail) {
|
||||
$self->{on_select_object}->($obj_idx);
|
||||
|
||||
if ($event->LeftDown) {
|
||||
# start dragging
|
||||
my $instance = $self->{model}->objects->[$obj_idx]->instances->[$instance_idx];
|
||||
my $instance_origin = [ map scale($_), @{$instance->offset} ];
|
||||
$self->{drag_start_pos} = [ # displacement between the click and the instance origin in scaled model units
|
||||
$point->x - $instance_origin->[X],
|
||||
$point->y - $instance_origin->[Y], #-
|
||||
];
|
||||
$self->{drag_object} = [ $obj_idx, $instance_idx ];
|
||||
} elsif ($event->RightDown) {
|
||||
$self->{on_right_click}->($pos->x, $pos->y);
|
||||
}
|
||||
|
||||
last OBJECTS;
|
||||
}
|
||||
}
|
||||
}
|
||||
$self->Refresh;
|
||||
} elsif ($event->LeftUp) {
|
||||
if ($self->{drag_object}) {
|
||||
$self->{on_instances_moved}->();
|
||||
}
|
||||
$self->{drag_start_pos} = undef;
|
||||
$self->{drag_object} = undef;
|
||||
$self->SetCursor(wxSTANDARD_CURSOR);
|
||||
} elsif ($event->LeftDClick) {
|
||||
$self->{on_double_click}->();
|
||||
} elsif ($event->Dragging) {
|
||||
return if !$self->{drag_start_pos}; # concurrency problems
|
||||
my ($obj_idx, $instance_idx) = @{ $self->{drag_object} };
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
$model_object->instances->[$instance_idx]->set_offset(
|
||||
Slic3r::Pointf->new(
|
||||
unscale($point->[X] - $self->{drag_start_pos}[X]),
|
||||
unscale($point->[Y] - $self->{drag_start_pos}[Y]),
|
||||
));
|
||||
$self->Refresh;
|
||||
} elsif ($event->Moving) {
|
||||
my $cursor = wxSTANDARD_CURSOR;
|
||||
if (defined first { $_->contour->contains_point($point) } map @$_, map @{$_->instance_thumbnails}, @{ $self->{objects} }) {
|
||||
$cursor = Wx::Cursor->new(wxCURSOR_HAND);
|
||||
}
|
||||
$self->SetCursor($cursor);
|
||||
}
|
||||
}
|
||||
|
||||
sub update_bed_size {
|
||||
my ($self) = @_;
|
||||
|
||||
# when the canvas is not rendered yet, its GetSize() method returns 0,0
|
||||
my $canvas_size = $self->GetSize;
|
||||
my ($canvas_w, $canvas_h) = ($canvas_size->GetWidth, $canvas_size->GetHeight);
|
||||
return if $canvas_w == 0;
|
||||
|
||||
# get bed shape polygon
|
||||
$self->{bed_polygon} = my $polygon = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape});
|
||||
my $bb = $polygon->bounding_box;
|
||||
my $size = $bb->size;
|
||||
|
||||
# calculate the scaling factor needed for constraining print bed area inside preview
|
||||
# scaling_factor is expressed in pixel / mm
|
||||
$self->{scaling_factor} = min($canvas_w / unscale($size->x), $canvas_h / unscale($size->y)); #)
|
||||
|
||||
# calculate the displacement needed to center bed
|
||||
$self->{bed_origin} = [
|
||||
$canvas_w/2 - (unscale($bb->x_max + $bb->x_min)/2 * $self->{scaling_factor}),
|
||||
$canvas_h - ($canvas_h/2 - (unscale($bb->y_max + $bb->y_min)/2 * $self->{scaling_factor})),
|
||||
];
|
||||
|
||||
# calculate print center
|
||||
my $center = $bb->center;
|
||||
$self->{print_center} = [ unscale($center->x), unscale($center->y) ]; #))
|
||||
|
||||
# cache bed contours and grid
|
||||
{
|
||||
my $step = scale 10; # 1cm grid
|
||||
my @polylines = ();
|
||||
for (my $x = $bb->x_min - ($bb->x_min % $step) + $step; $x < $bb->x_max; $x += $step) {
|
||||
push @polylines, Slic3r::Polyline->new([$x, $bb->y_min], [$x, $bb->y_max]);
|
||||
}
|
||||
for (my $y = $bb->y_min - ($bb->y_min % $step) + $step; $y < $bb->y_max; $y += $step) {
|
||||
push @polylines, Slic3r::Polyline->new([$bb->x_min, $y], [$bb->x_max, $y]);
|
||||
}
|
||||
@polylines = @{intersection_pl(\@polylines, [$polygon])};
|
||||
$self->{grid} = [ map $self->scaled_points_to_pixel([ @$_[0,-1] ], 1), @polylines ];
|
||||
}
|
||||
}
|
||||
|
||||
sub clean_instance_thumbnails {
|
||||
my ($self) = @_;
|
||||
|
||||
foreach my $object (@{ $self->{objects} }) {
|
||||
@{ $object->instance_thumbnails } = ();
|
||||
}
|
||||
}
|
||||
|
||||
# convert a model coordinate into a pixel coordinate
|
||||
sub unscaled_point_to_pixel {
|
||||
my ($self, $point) = @_;
|
||||
|
||||
my $canvas_height = $self->GetSize->GetHeight;
|
||||
my $zero = $self->{bed_origin};
|
||||
return [
|
||||
$point->[X] * $self->{scaling_factor} + $zero->[X],
|
||||
$canvas_height - $point->[Y] * $self->{scaling_factor} + ($zero->[Y] - $canvas_height),
|
||||
];
|
||||
}
|
||||
|
||||
sub scaled_points_to_pixel {
|
||||
my ($self, $points, $unscale) = @_;
|
||||
|
||||
my $result = [];
|
||||
foreach my $point (@$points) {
|
||||
$point = [ map unscale($_), @$point ] if $unscale;
|
||||
push @$result, $self->unscaled_point_to_pixel($point);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub point_to_model_units {
|
||||
my ($self, $point) = @_;
|
||||
|
||||
my $zero = $self->{bed_origin};
|
||||
return Slic3r::Point->new(
|
||||
scale ($point->[X] - $zero->[X]) / $self->{scaling_factor},
|
||||
scale ($zero->[Y] - $point->[Y]) / $self->{scaling_factor},
|
||||
);
|
||||
}
|
||||
|
||||
sub reload_scene {
|
||||
my ($self, $force) = @_;
|
||||
|
||||
if (! $self->IsShown && ! $force) {
|
||||
$self->{reload_delayed} = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
$self->{reload_delayed} = 0;
|
||||
|
||||
foreach my $obj_idx (0..$#{$self->{model}->objects}) {
|
||||
my $plater_object = $self->{objects}[$obj_idx];
|
||||
next if $plater_object->thumbnail;
|
||||
# The thumbnail is not valid, update it with a convex hull of an object.
|
||||
$plater_object->thumbnail(Slic3r::ExPolygon::Collection->new);
|
||||
$plater_object->make_thumbnail($self->{model}, $obj_idx);
|
||||
$plater_object->transform_thumbnail($self->{model}, $obj_idx);
|
||||
}
|
||||
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
# Called by the Platter wxNotebook when this page is activated.
|
||||
sub OnActivate {
|
||||
my ($self) = @_;
|
||||
$self->reload_scene(1) if ($self->{reload_delayed});
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,916 +0,0 @@
|
|||
# 2D preview of the tool paths of a single layer, using a thin line.
|
||||
# OpenGL is used to render the paths.
|
||||
# Vojtech also added a 2D simulation of under/over extrusion in a single layer.
|
||||
|
||||
package Slic3r::GUI::Plater::2DToolpaths;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Slic3r::Print::State ':steps';
|
||||
use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE wxWANTS_CHARS);
|
||||
use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN);
|
||||
use base qw(Wx::Panel Class::Accessor);
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(print enabled));
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $print) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
|
||||
$self->SetBackgroundColour(wxWHITE);
|
||||
|
||||
# init GUI elements
|
||||
my $canvas = $self->{canvas} = Slic3r::GUI::Plater::2DToolpaths::Canvas->new($self, $print);
|
||||
my $slider = $self->{slider} = Wx::Slider->new(
|
||||
$self, -1,
|
||||
0, # default
|
||||
0, # min
|
||||
# we set max to a bogus non-zero value because the MSW implementation of wxSlider
|
||||
# will skip drawing the slider if max <= min:
|
||||
1, # max
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxVERTICAL | wxSL_INVERSE,
|
||||
);
|
||||
my $z_label = $self->{z_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
[40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
$z_label->SetFont($Slic3r::GUI::small_font);
|
||||
|
||||
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$vsizer->Add($slider, 1, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
|
||||
$vsizer->Add($z_label, 0, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$sizer->Add($canvas, 1, wxALL | wxEXPAND, 0);
|
||||
$sizer->Add($vsizer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5);
|
||||
|
||||
EVT_SLIDER($self, $slider, sub {
|
||||
$self->set_z($self->{layers_z}[$slider->GetValue])
|
||||
if $self->enabled;
|
||||
});
|
||||
EVT_KEY_DOWN($canvas, sub {
|
||||
my ($s, $event) = @_;
|
||||
if ($event->HasModifiers) {
|
||||
$event->Skip;
|
||||
} else {
|
||||
my $key = $event->GetKeyCode;
|
||||
if ($key == ord('D') || $key == WXK_LEFT) {
|
||||
# Keys: 'D' or WXK_LEFT
|
||||
$slider->SetValue($slider->GetValue - 1);
|
||||
$self->set_z($self->{layers_z}[$slider->GetValue]);
|
||||
} elsif ($key == ord('U') || $key == WXK_RIGHT) {
|
||||
# Keys: 'U' or WXK_RIGHT
|
||||
$slider->SetValue($slider->GetValue + 1);
|
||||
$self->set_z($self->{layers_z}[$slider->GetValue]);
|
||||
} elsif ($key >= ord('1') && $key <= ord('3')) {
|
||||
# Keys: '1' to '3'
|
||||
$canvas->set_simulation_mode($key - ord('1'));
|
||||
} else {
|
||||
$event->Skip;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$self->SetSizer($sizer);
|
||||
$self->SetMinSize($self->GetSize);
|
||||
$sizer->SetSizeHints($self);
|
||||
|
||||
# init print
|
||||
$self->{print} = $print;
|
||||
$self->reload_print;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub reload_print {
|
||||
my ($self) = @_;
|
||||
|
||||
# we require that there's at least one object and the posSlice step
|
||||
# is performed on all of them (this ensures that _shifted_copies was
|
||||
# populated and we know the number of layers)
|
||||
if (!$self->print->object_step_done(STEP_SLICE)) {
|
||||
$self->enabled(0);
|
||||
$self->{slider}->Hide;
|
||||
$self->{canvas}->Refresh; # clears canvas
|
||||
return;
|
||||
}
|
||||
|
||||
$self->{canvas}->bb($self->print->total_bounding_box);
|
||||
$self->{canvas}->_dirty(1);
|
||||
|
||||
my %z = (); # z => 1
|
||||
foreach my $object (@{$self->{print}->objects}) {
|
||||
foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
|
||||
$z{$layer->print_z} = 1;
|
||||
}
|
||||
}
|
||||
$self->enabled(1);
|
||||
$self->{layers_z} = [ sort { $a <=> $b } keys %z ];
|
||||
$self->{slider}->SetRange(0, scalar(@{$self->{layers_z}})-1);
|
||||
if ((my $z_idx = $self->{slider}->GetValue) <= $#{$self->{layers_z}}) {
|
||||
$self->set_z($self->{layers_z}[$z_idx]);
|
||||
} else {
|
||||
$self->{slider}->SetValue(0);
|
||||
$self->set_z($self->{layers_z}[0]) if @{$self->{layers_z}};
|
||||
}
|
||||
$self->{slider}->Show;
|
||||
$self->Layout;
|
||||
}
|
||||
|
||||
sub set_z {
|
||||
my ($self, $z) = @_;
|
||||
|
||||
return if !$self->enabled;
|
||||
$self->{z_label}->SetLabel(sprintf '%.2f', $z);
|
||||
$self->{canvas}->set_z($z);
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::Plater::2DToolpaths::Canvas;
|
||||
|
||||
use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
|
||||
use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
|
||||
use base qw(Wx::GLCanvas Class::Accessor);
|
||||
use Wx::GLCanvas qw(:all);
|
||||
use List::Util qw(min max first);
|
||||
use Slic3r::Geometry qw(scale epsilon X Y);
|
||||
use Slic3r::Print::State ':steps';
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(
|
||||
print z layers color init
|
||||
bb
|
||||
_camera_bb
|
||||
_dirty
|
||||
_zoom
|
||||
_camera_target
|
||||
_drag_start_xy
|
||||
_texture_name
|
||||
_texture_size
|
||||
_extrusion_simulator
|
||||
_simulation_mode
|
||||
));
|
||||
|
||||
# make OpenGL::Array thread-safe
|
||||
{
|
||||
no warnings 'redefine';
|
||||
*OpenGL::Array::CLONE_SKIP = sub { 1 };
|
||||
}
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, $print) = @_;
|
||||
|
||||
my $self = (Wx::wxVERSION >= 3.000003) ?
|
||||
# The wxWidgets 3.0.3-beta have a bug, they crash with NULL attribute list.
|
||||
$class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "",
|
||||
[WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, 0]) :
|
||||
$class->SUPER::new($parent);
|
||||
# Immediatelly force creation of the OpenGL context to consume the static variable s_wglContextAttribs.
|
||||
$self->GetContext();
|
||||
$self->print($print);
|
||||
$self->_zoom(1);
|
||||
|
||||
# 2D point in model space
|
||||
$self->_camera_target(Slic3r::Pointf->new(0,0));
|
||||
|
||||
# Texture for the extrusion simulator. The texture will be allocated / reallocated on Resize.
|
||||
$self->_texture_name(0);
|
||||
$self->_texture_size(Slic3r::Point->new(0,0));
|
||||
$self->_extrusion_simulator(Slic3r::ExtrusionSimulator->new());
|
||||
$self->_simulation_mode(0);
|
||||
|
||||
EVT_PAINT($self, sub {
|
||||
my $dc = Wx::PaintDC->new($self);
|
||||
$self->Render($dc);
|
||||
});
|
||||
EVT_SIZE($self, sub { $self->_dirty(1) });
|
||||
EVT_IDLE($self, sub {
|
||||
return unless $self->_dirty;
|
||||
return if !$self->IsShownOnScreen;
|
||||
$self->Resize;
|
||||
$self->Refresh;
|
||||
});
|
||||
EVT_MOUSEWHEEL($self, sub {
|
||||
my ($self, $e) = @_;
|
||||
|
||||
return if !$self->GetParent->enabled;
|
||||
|
||||
my $old_zoom = $self->_zoom;
|
||||
|
||||
# Calculate the zoom delta and apply it to the current zoom factor
|
||||
my $zoom = -$e->GetWheelRotation() / $e->GetWheelDelta();
|
||||
$zoom = max(min($zoom, 4), -4);
|
||||
$zoom /= 10;
|
||||
$self->_zoom($self->_zoom / (1-$zoom));
|
||||
$self->_zoom(1.25) if $self->_zoom > 1.25; # prevent from zooming out too much
|
||||
|
||||
{
|
||||
# In order to zoom around the mouse point we need to translate
|
||||
# the camera target. This math is almost there but not perfect yet...
|
||||
my $camera_bb_size = $self->_camera_bb->size;
|
||||
my $size = Slic3r::Pointf->new($self->GetSizeWH);
|
||||
my $pos = Slic3r::Pointf->new($e->GetPositionXY);
|
||||
|
||||
# calculate the zooming center in pixel coordinates relative to the viewport center
|
||||
my $vec = Slic3r::Pointf->new($pos->x - $size->x/2, $pos->y - $size->y/2); #-
|
||||
|
||||
# calculate where this point will end up after applying the new zoom
|
||||
my $vec2 = $vec->clone;
|
||||
$vec2->scale($old_zoom / $self->_zoom);
|
||||
|
||||
# move the camera target by the difference of the two positions
|
||||
$self->_camera_target->translate(
|
||||
-($vec->x - $vec2->x) * $camera_bb_size->x / $size->x,
|
||||
($vec->y - $vec2->y) * $camera_bb_size->y / $size->y, #//
|
||||
);
|
||||
}
|
||||
|
||||
$self->_dirty(1);
|
||||
});
|
||||
EVT_MOUSE_EVENTS($self, \&mouse_event);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub Destroy {
|
||||
my ($self) = @_;
|
||||
|
||||
# Deallocate the OpenGL resources.
|
||||
my $context = $self->GetContext;
|
||||
if ($context and $self->texture_id) {
|
||||
$self->SetCurrent($context);
|
||||
glDeleteTextures(1, ($self->texture_id));
|
||||
$self->SetCurrent(0);
|
||||
$self->texture_id(0);
|
||||
$self->texture_size(new Slic3r::Point(0, 0));
|
||||
}
|
||||
return $self->SUPER::Destroy;
|
||||
}
|
||||
|
||||
sub mouse_event {
|
||||
my ($self, $e) = @_;
|
||||
|
||||
return if !$self->GetParent->enabled;
|
||||
|
||||
my $pos = Slic3r::Pointf->new($e->GetPositionXY);
|
||||
if ($e->Entering && (&Wx::wxMSW || $^O eq 'linux')) {
|
||||
# wxMSW and Linux needs focus in order to catch key events
|
||||
$self->SetFocus;
|
||||
} elsif ($e->Dragging) {
|
||||
if ($e->LeftIsDown || $e->MiddleIsDown || $e->RightIsDown) {
|
||||
# if dragging, translate view
|
||||
|
||||
if (defined $self->_drag_start_xy) {
|
||||
my $move = $self->_drag_start_xy->vector_to($pos); # in pixels
|
||||
|
||||
# get viewport and camera size in order to convert pixel to model units
|
||||
my ($x, $y) = $self->GetSizeWH;
|
||||
my $camera_bb_size = $self->_camera_bb->size;
|
||||
|
||||
# compute translation in model units
|
||||
$self->_camera_target->translate(
|
||||
-$move->x * $camera_bb_size->x / $x,
|
||||
$move->y * $camera_bb_size->y / $y, # /**
|
||||
);
|
||||
|
||||
$self->_dirty(1);
|
||||
}
|
||||
$self->_drag_start_xy($pos);
|
||||
}
|
||||
} elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) {
|
||||
$self->_drag_start_xy(undef);
|
||||
} else {
|
||||
$e->Skip();
|
||||
}
|
||||
}
|
||||
|
||||
sub set_z {
|
||||
my ($self, $z) = @_;
|
||||
|
||||
my $print = $self->print;
|
||||
|
||||
# can we have interlaced layers?
|
||||
my $interlaced = (defined first { $_->config->support_material } @{$print->objects})
|
||||
|| (defined first { $_->config->infill_every_layers > 1 } @{$print->regions});
|
||||
|
||||
my $max_layer_height = $print->max_allowed_layer_height;
|
||||
|
||||
my @layers = ();
|
||||
foreach my $object (@{$print->objects}) {
|
||||
foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
|
||||
if ($interlaced) {
|
||||
push @layers, $layer
|
||||
if $z > ($layer->print_z - $max_layer_height - epsilon)
|
||||
&& $z <= $layer->print_z + epsilon;
|
||||
} else {
|
||||
push @layers, $layer if abs($layer->print_z - $z) < epsilon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# reverse layers so that we draw the lowermost (i.e. current) on top
|
||||
$self->z($z);
|
||||
$self->layers([ reverse @layers ]);
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
sub set_simulation_mode
|
||||
{
|
||||
my ($self, $mode) = @_;
|
||||
$self->_simulation_mode($mode);
|
||||
$self->_dirty(1);
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
sub Render {
|
||||
my ($self, $dc) = @_;
|
||||
|
||||
# prevent calling SetCurrent() when window is not shown yet
|
||||
return unless $self->IsShownOnScreen;
|
||||
return unless my $context = $self->GetContext;
|
||||
$self->SetCurrent($context);
|
||||
$self->InitGL;
|
||||
|
||||
glClearColor(1, 1, 1, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
if (!$self->GetParent->enabled || !$self->layers) {
|
||||
$self->SwapBuffers;
|
||||
return;
|
||||
}
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
if ($self->_simulation_mode and $self->_texture_name and $self->_texture_size->x() > 0 and $self->_texture_size->y() > 0) {
|
||||
$self->_simulate_extrusion();
|
||||
my ($x, $y) = $self->GetSizeWH;
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_REPLACE);
|
||||
glBindTexture(GL_TEXTURE_2D, $self->_texture_name);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexImage2D_c(GL_TEXTURE_2D,
|
||||
0, # level (0 normal, heighr is form mip-mapping)
|
||||
GL_RGBA, # internal format
|
||||
$self->_texture_size->x(), $self->_texture_size->y(),
|
||||
0, # border
|
||||
GL_RGBA, # format RGBA color data
|
||||
GL_UNSIGNED_BYTE, # unsigned byte data
|
||||
$self->_extrusion_simulator->image_ptr()); # ptr to texture data
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
glOrtho(0, 1, 0, 1, 0, 1);
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2f(0, 0);
|
||||
glVertex2f(0, 0);
|
||||
glTexCoord2f($x/$self->_texture_size->x(), 0);
|
||||
glVertex2f(1, 0);
|
||||
glTexCoord2f($x/$self->_texture_size->x(), $y/$self->_texture_size->y());
|
||||
glVertex2f(1, 1);
|
||||
glTexCoord2f(0, $y/$self->_texture_size->y());
|
||||
glVertex2f(0, 1);
|
||||
glEnd();
|
||||
glPopMatrix();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
# anti-alias
|
||||
if (0) {
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
|
||||
glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE);
|
||||
}
|
||||
|
||||
# Tesselator triangulates polygons with holes on the fly for the rendering purposes only.
|
||||
my $tess;
|
||||
if ($self->_simulation_mode() == 0 and !(&Wx::wxMSW && $OpenGL::VERSION < 0.6704)) {
|
||||
# We can't use the GLU tesselator on MSW with older OpenGL versions
|
||||
# because of an upstream bug:
|
||||
# http://sourceforge.net/p/pogl/bugs/16/
|
||||
$tess = gluNewTess();
|
||||
gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT');
|
||||
gluTessCallback($tess, GLU_TESS_END, 'DEFAULT');
|
||||
gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT');
|
||||
gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT');
|
||||
gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT');
|
||||
gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT');
|
||||
}
|
||||
|
||||
foreach my $layer (@{$self->layers}) {
|
||||
my $object = $layer->object;
|
||||
|
||||
# only draw the slice for the current layer
|
||||
next unless abs($layer->print_z - $self->z) < epsilon;
|
||||
|
||||
# draw slice contour
|
||||
glLineWidth(1);
|
||||
foreach my $copy (@{ $object->_shifted_copies }) {
|
||||
glPushMatrix();
|
||||
glTranslatef(@$copy, 0);
|
||||
|
||||
foreach my $slice (@{$layer->slices}) {
|
||||
glColor3f(0.95, 0.95, 0.95);
|
||||
|
||||
if ($tess) {
|
||||
gluTessBeginPolygon($tess);
|
||||
foreach my $polygon (@$slice) {
|
||||
gluTessBeginContour($tess);
|
||||
gluTessVertex_p($tess, @$_, 0) for @$polygon;
|
||||
gluTessEndContour($tess);
|
||||
}
|
||||
gluTessEndPolygon($tess);
|
||||
}
|
||||
|
||||
glColor3f(0.9, 0.9, 0.9);
|
||||
foreach my $polygon (@$slice) {
|
||||
foreach my $line (@{$polygon->lines}) {
|
||||
glBegin(GL_LINES);
|
||||
glVertex2f(@{$line->a});
|
||||
glVertex2f(@{$line->b});
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
glPopMatrix();
|
||||
}
|
||||
}
|
||||
|
||||
my $skirt_drawn = 0;
|
||||
my $brim_drawn = 0;
|
||||
foreach my $layer (@{$self->layers}) {
|
||||
my $object = $layer->object;
|
||||
my $print_z = $layer->print_z;
|
||||
|
||||
# draw brim
|
||||
if ($self->print->step_done(STEP_BRIM) && $layer->id == 0 && !$brim_drawn) {
|
||||
$self->color([0, 0, 0]);
|
||||
$self->_draw(undef, $print_z, $_) for @{$self->print->brim};
|
||||
$brim_drawn = 1;
|
||||
}
|
||||
if ($self->print->step_done(STEP_SKIRT)
|
||||
&& ($self->print->has_infinite_skirt() || $self->print->config->skirt_height > $layer->id)
|
||||
&& !$skirt_drawn) {
|
||||
$self->color([0, 0, 0]);
|
||||
$self->_draw(undef, $print_z, $_) for @{$self->print->skirt};
|
||||
$skirt_drawn = 1;
|
||||
}
|
||||
|
||||
foreach my $layerm (@{$layer->regions}) {
|
||||
if ($object->step_done(STEP_PERIMETERS)) {
|
||||
$self->color([0.7, 0, 0]);
|
||||
$self->_draw($object, $print_z, $_) for map @$_, @{$layerm->perimeters};
|
||||
}
|
||||
|
||||
if ($object->step_done(STEP_INFILL)) {
|
||||
$self->color([0, 0, 0.7]);
|
||||
$self->_draw($object, $print_z, $_) for map @$_, @{$layerm->fills};
|
||||
}
|
||||
}
|
||||
|
||||
if ($object->step_done(STEP_SUPPORTMATERIAL)) {
|
||||
if ($layer->isa('Slic3r::Layer::Support')) {
|
||||
$self->color([0, 0, 0]);
|
||||
$self->_draw($object, $print_z, $_) for @{$layer->support_fills};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gluDeleteTess($tess) if $tess;
|
||||
$self->SwapBuffers;
|
||||
}
|
||||
|
||||
sub _draw {
|
||||
my ($self, $object, $print_z, $path) = @_;
|
||||
|
||||
my @paths = ($path->isa('Slic3r::ExtrusionLoop') || $path->isa('Slic3r::ExtrusionMultiPath'))
|
||||
? @$path
|
||||
: ($path);
|
||||
|
||||
$self->_draw_path($object, $print_z, $_) for @paths;
|
||||
}
|
||||
|
||||
sub _draw_path {
|
||||
my ($self, $object, $print_z, $path) = @_;
|
||||
|
||||
return if $print_z - $path->height > $self->z - epsilon;
|
||||
|
||||
if (abs($print_z - $self->z) < epsilon) {
|
||||
glColor3f(@{$self->color});
|
||||
} else {
|
||||
glColor3f(0.8, 0.8, 0.8);
|
||||
}
|
||||
|
||||
glLineWidth(1);
|
||||
|
||||
if (defined $object) {
|
||||
foreach my $copy (@{ $object->_shifted_copies }) {
|
||||
glPushMatrix();
|
||||
glTranslatef(@$copy, 0);
|
||||
foreach my $line (@{$path->polyline->lines}) {
|
||||
glBegin(GL_LINES);
|
||||
glVertex2f(@{$line->a});
|
||||
glVertex2f(@{$line->b});
|
||||
glEnd();
|
||||
}
|
||||
glPopMatrix();
|
||||
}
|
||||
} else {
|
||||
foreach my $line (@{$path->polyline->lines}) {
|
||||
glBegin(GL_LINES);
|
||||
glVertex2f(@{$line->a});
|
||||
glVertex2f(@{$line->b});
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub _simulate_extrusion {
|
||||
my ($self) = @_;
|
||||
$self->_extrusion_simulator->reset_accumulator();
|
||||
foreach my $layer (@{$self->layers}) {
|
||||
if (abs($layer->print_z - $self->z) < epsilon) {
|
||||
my $object = $layer->object;
|
||||
my @shifts = (defined $object) ? @{$object->_shifted_copies} : (Slic3r::Point->new(0, 0));
|
||||
foreach my $layerm (@{$layer->regions}) {
|
||||
my @extrusions = ();
|
||||
if ($object->step_done(STEP_PERIMETERS)) {
|
||||
push @extrusions, @$_ for @{$layerm->perimeters};
|
||||
}
|
||||
if ($object->step_done(STEP_INFILL)) {
|
||||
push @extrusions, @$_ for @{$layerm->fills};
|
||||
}
|
||||
foreach my $extrusion_entity (@extrusions) {
|
||||
my @paths = ($extrusion_entity->isa('Slic3r::ExtrusionLoop') || $extrusion_entity->isa('Slic3r::ExtrusionMultiPath'))
|
||||
? @$extrusion_entity
|
||||
: ($extrusion_entity);
|
||||
foreach my $path (@paths) {
|
||||
print "width: ", $path->width,
|
||||
" height: ", $path->height,
|
||||
" mm3_per_mm: ", $path->mm3_per_mm,
|
||||
" height2: ", $path->mm3_per_mm / $path->height,
|
||||
"\n";
|
||||
$self->_extrusion_simulator->extrude_to_accumulator($path, $_, $self->_simulation_mode()) for @shifts;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$self->_extrusion_simulator->evaluate_accumulator($self->_simulation_mode());
|
||||
}
|
||||
|
||||
sub InitGL {
|
||||
my $self = shift;
|
||||
|
||||
return if $self->init;
|
||||
return unless $self->GetContext;
|
||||
|
||||
my $texture_id = 0;
|
||||
($texture_id) = glGenTextures_p(1);
|
||||
$self->_texture_name($texture_id);
|
||||
$self->init(1);
|
||||
}
|
||||
|
||||
sub GetContext {
|
||||
my ($self) = @_;
|
||||
return $self->{context} ||= Wx::GLContext->new($self);
|
||||
}
|
||||
|
||||
sub SetCurrent {
|
||||
my ($self, $context) = @_;
|
||||
return $self->SUPER::SetCurrent($context);
|
||||
}
|
||||
|
||||
sub Resize {
|
||||
my ($self) = @_;
|
||||
|
||||
return unless $self->GetContext;
|
||||
return unless $self->bb;
|
||||
$self->_dirty(0);
|
||||
|
||||
$self->SetCurrent($self->GetContext);
|
||||
my ($x, $y) = $self->GetSizeWH;
|
||||
|
||||
if ($self->_texture_size->x() < $x or $self->_texture_size->y() < $y) {
|
||||
# Allocate a large enough OpenGL texture with power of 2 dimensions.
|
||||
$self->_texture_size->set_x(1) if ($self->_texture_size->x() == 0);
|
||||
$self->_texture_size->set_y(1) if ($self->_texture_size->y() == 0);
|
||||
$self->_texture_size->set_x($self->_texture_size->x() * 2) while ($self->_texture_size->x() < $x);
|
||||
$self->_texture_size->set_y($self->_texture_size->y() * 2) while ($self->_texture_size->y() < $y);
|
||||
#print "screen size ", $x, "x", $y;
|
||||
#print "texture size ", $self->_texture_size->x(), "x", $self->_texture_size->y();
|
||||
# Initialize an empty texture.
|
||||
glBindTexture(GL_TEXTURE_2D, $self->_texture_name);
|
||||
if (1) {
|
||||
glTexImage2D_c(GL_TEXTURE_2D,
|
||||
0, # level (0 normal, heighr is form mip-mapping)
|
||||
GL_RGBA, # internal format
|
||||
$self->_texture_size->x(), $self->_texture_size->y(),
|
||||
0, # border
|
||||
GL_RGBA, # format RGBA color data
|
||||
GL_UNSIGNED_BYTE, # unsigned byte data
|
||||
0); # ptr to texture data
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
$self->_extrusion_simulator->set_image_size($self->_texture_size);
|
||||
}
|
||||
$self->_extrusion_simulator->set_viewport(Slic3r::Geometry::BoundingBox->new_from_points(
|
||||
[Slic3r::Point->new(0, 0), Slic3r::Point->new($x, $y)]));
|
||||
|
||||
glViewport(0, 0, $x, $y);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
|
||||
my $bb = $self->bb->clone;
|
||||
|
||||
# rescale in dependence of window aspect ratio
|
||||
my $bb_size = $bb->size;
|
||||
my $ratio_x = ($x != 0.0) ? $bb_size->x / $x : 1.0;
|
||||
my $ratio_y = ($y != 0.0) ? $bb_size->y / $y : 1.0;
|
||||
|
||||
if ($ratio_y < $ratio_x) {
|
||||
if ($ratio_y != 0.0) {
|
||||
my $new_size_y = $bb_size->y * $ratio_x / $ratio_y;
|
||||
my $half_delta_size_y = 0.5 * ($new_size_y - $bb_size->y);
|
||||
$bb->set_y_min($bb->y_min - $half_delta_size_y);
|
||||
$bb->set_y_max($bb->y_max + $half_delta_size_y);
|
||||
}
|
||||
} elsif ($ratio_x < $ratio_y) {
|
||||
if ($ratio_x != 0.0) {
|
||||
my $new_size_x = $bb_size->x * $ratio_y / $ratio_x;
|
||||
my $half_delta_size_x = 0.5 * ($new_size_x - $bb_size->x);
|
||||
$bb->set_x_min($bb->x_min - $half_delta_size_x);
|
||||
$bb->set_x_max($bb->x_max + $half_delta_size_x);
|
||||
}
|
||||
}
|
||||
|
||||
# center bounding box around origin before scaling it
|
||||
my $bb_center = $bb->center;
|
||||
$bb->translate(@{$bb_center->negative});
|
||||
|
||||
# scale bounding box according to zoom factor
|
||||
$bb->scale($self->_zoom);
|
||||
|
||||
# reposition bounding box around original center
|
||||
$bb->translate(@{$bb_center});
|
||||
|
||||
# translate camera
|
||||
$bb->translate(@{$self->_camera_target});
|
||||
|
||||
# # keep camera_bb within total bb
|
||||
# # (i.e. prevent user from panning outside the bounding box)
|
||||
# {
|
||||
# my @translate = (0,0);
|
||||
# if ($bb->x_min < $self->bb->x_min) {
|
||||
# $translate[X] += $self->bb->x_min - $bb->x_min;
|
||||
# }
|
||||
# if ($bb->y_min < $self->bb->y_min) {
|
||||
# $translate[Y] += $self->bb->y_min - $bb->y_min;
|
||||
# }
|
||||
# if ($bb->x_max > $self->bb->x_max) {
|
||||
# $translate[X] -= $bb->x_max - $self->bb->x_max;
|
||||
# }
|
||||
# if ($bb->y_max > $self->bb->y_max) {
|
||||
# $translate[Y] -= $bb->y_max - $self->bb->y_max;
|
||||
# }
|
||||
# $self->_camera_target->translate(@translate);
|
||||
# $bb->translate(@translate);
|
||||
# }
|
||||
|
||||
# save camera
|
||||
$self->_camera_bb($bb);
|
||||
|
||||
my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max);
|
||||
if (($x2 - $x1)/($y2 - $y1) > $x/$y) {
|
||||
# adjust Y
|
||||
my $new_y = $y * ($x2 - $x1) / $x;
|
||||
$y1 = ($y2 + $y1)/2 - $new_y/2;
|
||||
$y2 = $y1 + $new_y;
|
||||
} else {
|
||||
my $new_x = $x * ($y2 - $y1) / $y;
|
||||
$x1 = ($x2 + $x1)/2 - $new_x/2;
|
||||
$x2 = $x1 + $new_x;
|
||||
}
|
||||
glOrtho($x1, $x2, $y1, $y2, 0, 1);
|
||||
|
||||
# Set the adjusted bounding box at the extrusion simulator.
|
||||
#print "Scene bbox ", $bb->x_min, ",", $bb->y_min, " ", $bb->x_max, ",", $bb->y_max, "\n";
|
||||
#print "Setting simulator bbox ", $x1, ",", $y1, " ", $x2, ",", $y2, "\n";
|
||||
$self->_extrusion_simulator->set_bounding_box(
|
||||
Slic3r::Geometry::BoundingBox->new_from_points(
|
||||
[Slic3r::Point->new($x1, $y1), Slic3r::Point->new($x2, $y2)]));
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
}
|
||||
|
||||
# Thick line drawing is not used anywhere. Probably not tested?
|
||||
sub line {
|
||||
my (
|
||||
$x1, $y1, $x2, $y2, # coordinates of the line
|
||||
$w, # width/thickness of the line in pixel
|
||||
$Cr, $Cg, $Cb, # RGB color components
|
||||
$Br, $Bg, $Bb, # color of background when alphablend=false
|
||||
# Br=alpha of color when alphablend=true
|
||||
$alphablend, # use alpha blend or not
|
||||
) = @_;
|
||||
|
||||
my $t;
|
||||
my $R;
|
||||
my $f = $w - int($w);
|
||||
my $A;
|
||||
|
||||
if ($alphablend) {
|
||||
$A = $Br;
|
||||
} else {
|
||||
$A = 1;
|
||||
}
|
||||
|
||||
# determine parameters t,R
|
||||
if ($w >= 0 && $w < 1) {
|
||||
$t = 0.05; $R = 0.48 + 0.32 * $f;
|
||||
if (!$alphablend) {
|
||||
$Cr += 0.88 * (1-$f);
|
||||
$Cg += 0.88 * (1-$f);
|
||||
$Cb += 0.88 * (1-$f);
|
||||
$Cr = 1.0 if ($Cr > 1.0);
|
||||
$Cg = 1.0 if ($Cg > 1.0);
|
||||
$Cb = 1.0 if ($Cb > 1.0);
|
||||
} else {
|
||||
$A *= $f;
|
||||
}
|
||||
} elsif ($w >= 1.0 && $w < 2.0) {
|
||||
$t = 0.05 + $f*0.33; $R = 0.768 + 0.312*$f;
|
||||
} elsif ($w >= 2.0 && $w < 3.0) {
|
||||
$t = 0.38 + $f*0.58; $R = 1.08;
|
||||
} elsif ($w >= 3.0 && $w < 4.0) {
|
||||
$t = 0.96 + $f*0.48; $R = 1.08;
|
||||
} elsif ($w >= 4.0 && $w < 5.0) {
|
||||
$t= 1.44 + $f*0.46; $R = 1.08;
|
||||
} elsif ($w >= 5.0 && $w < 6.0) {
|
||||
$t= 1.9 + $f*0.6; $R = 1.08;
|
||||
} elsif ($w >= 6.0) {
|
||||
my $ff = $w - 6.0;
|
||||
$t = 2.5 + $ff*0.50; $R = 1.08;
|
||||
}
|
||||
#printf( "w=%f, f=%f, C=%.4f\n", $w, $f, $C);
|
||||
|
||||
# determine angle of the line to horizontal
|
||||
my $tx = 0; my $ty = 0; # core thinkness of a line
|
||||
my $Rx = 0; my $Ry = 0; # fading edge of a line
|
||||
my $cx = 0; my $cy = 0; # cap of a line
|
||||
my $ALW = 0.01;
|
||||
my $dx = $x2 - $x1;
|
||||
my $dy = $y2 - $y1;
|
||||
if (abs($dx) < $ALW) {
|
||||
# vertical
|
||||
$tx = $t; $ty = 0;
|
||||
$Rx = $R; $Ry = 0;
|
||||
if ($w > 0.0 && $w < 1.0) {
|
||||
$tx *= 8;
|
||||
} elsif ($w == 1.0) {
|
||||
$tx *= 10;
|
||||
}
|
||||
} elsif (abs($dy) < $ALW) {
|
||||
#horizontal
|
||||
$tx = 0; $ty = $t;
|
||||
$Rx = 0; $Ry = $R;
|
||||
if ($w > 0.0 && $w < 1.0) {
|
||||
$ty *= 8;
|
||||
} elsif ($w == 1.0) {
|
||||
$ty *= 10;
|
||||
}
|
||||
} else {
|
||||
if ($w < 3) { # approximate to make things even faster
|
||||
my $m = $dy/$dx;
|
||||
# and calculate tx,ty,Rx,Ry
|
||||
if ($m > -0.4142 && $m <= 0.4142) {
|
||||
# -22.5 < $angle <= 22.5, approximate to 0 (degree)
|
||||
$tx = $t * 0.1; $ty = $t;
|
||||
$Rx = $R * 0.6; $Ry = $R;
|
||||
} elsif ($m > 0.4142 && $m <= 2.4142) {
|
||||
# 22.5 < $angle <= 67.5, approximate to 45 (degree)
|
||||
$tx = $t * -0.7071; $ty = $t * 0.7071;
|
||||
$Rx = $R * -0.7071; $Ry = $R * 0.7071;
|
||||
} elsif ($m > 2.4142 || $m <= -2.4142) {
|
||||
# 67.5 < $angle <= 112.5, approximate to 90 (degree)
|
||||
$tx = $t; $ty = $t*0.1;
|
||||
$Rx = $R; $Ry = $R*0.6;
|
||||
} elsif ($m > -2.4142 && $m < -0.4142) {
|
||||
# 112.5 < angle < 157.5, approximate to 135 (degree)
|
||||
$tx = $t * 0.7071; $ty = $t * 0.7071;
|
||||
$Rx = $R * 0.7071; $Ry = $R * 0.7071;
|
||||
} else {
|
||||
# error in determining angle
|
||||
printf("error in determining angle: m=%.4f\n", $m);
|
||||
}
|
||||
} else { # calculate to exact
|
||||
$dx= $y1 - $y2;
|
||||
$dy= $x2 - $x1;
|
||||
my $L = sqrt($dx*$dx + $dy*$dy);
|
||||
$dx /= $L;
|
||||
$dy /= $L;
|
||||
$cx = -0.6*$dy; $cy=0.6*$dx;
|
||||
$tx = $t*$dx; $ty = $t*$dy;
|
||||
$Rx = $R*$dx; $Ry = $R*$dy;
|
||||
}
|
||||
}
|
||||
|
||||
# draw the line by triangle strip
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
if (!$alphablend) {
|
||||
glColor3f($Br, $Bg, $Bb);
|
||||
} else {
|
||||
glColor4f($Cr, $Cg, $Cb, 0);
|
||||
}
|
||||
glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry); # fading edge
|
||||
glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
|
||||
|
||||
if (!$alphablend) {
|
||||
glColor3f($Cr, $Cg, $Cb);
|
||||
} else {
|
||||
glColor4f($Cr, $Cg, $Cb, $A);
|
||||
}
|
||||
glVertex2f($x1 - $tx, $y1 - $ty); # core
|
||||
glVertex2f($x2 - $tx, $y2 - $ty);
|
||||
glVertex2f($x1 + $tx, $y1 + $ty);
|
||||
glVertex2f($x2 + $tx, $y2 + $ty);
|
||||
|
||||
if ((abs($dx) < $ALW || abs($dy) < $ALW) && $w <= 1.0) {
|
||||
# printf("skipped one fading edge\n");
|
||||
} else {
|
||||
if (!$alphablend) {
|
||||
glColor3f($Br, $Bg, $Bb);
|
||||
} else {
|
||||
glColor4f($Cr, $Cg, $Cb, 0);
|
||||
}
|
||||
glVertex2f($x1 + $tx+ $Rx, $y1 + $ty + $Ry); # fading edge
|
||||
glVertex2f($x2 + $tx+ $Rx, $y2 + $ty + $Ry);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
# cap
|
||||
if ($w < 3) {
|
||||
# do not draw cap
|
||||
} else {
|
||||
# draw cap
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
if (!$alphablend) {
|
||||
glColor3f($Br, $Bg, $Bb);
|
||||
} else {
|
||||
glColor4f($Cr, $Cg, $Cb, 0);
|
||||
}
|
||||
glVertex2f($x1 - $Rx + $cx, $y1 - $Ry + $cy);
|
||||
glVertex2f($x1 + $Rx + $cx, $y1 + $Ry + $cy);
|
||||
glColor3f($Cr, $Cg, $Cb);
|
||||
glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry);
|
||||
glVertex2f($x1 + $tx + $Rx, $y1 + $ty + $Ry);
|
||||
glEnd();
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
if (!$alphablend) {
|
||||
glColor3f($Br, $Bg, $Bb);
|
||||
} else {
|
||||
glColor4f($Cr, $Cg, $Cb, 0);
|
||||
}
|
||||
glVertex2f($x2 - $Rx - $cx, $y2 - $Ry - $cy);
|
||||
glVertex2f($x2 + $Rx - $cx, $y2 + $Ry - $cy);
|
||||
glColor3f($Cr, $Cg, $Cb);
|
||||
glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
|
||||
glVertex2f($x2 + $tx + $Rx, $y2 + $ty + $Ry);
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
package Slic3r::GUI::Plater::2DToolpaths::Dialog;
|
||||
|
||||
use Wx qw(:dialog :id :misc :sizer);
|
||||
use Wx::Event qw(EVT_CLOSE);
|
||||
use base 'Wx::Dialog';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $print) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, "Toolpaths", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$sizer->Add(Slic3r::GUI::Plater::2DToolpaths->new($self, $print), 1, wxEXPAND, 0);
|
||||
$self->SetSizer($sizer);
|
||||
$self->SetMinSize($self->GetSize);
|
||||
|
||||
# needed to actually free memory
|
||||
EVT_CLOSE($self, sub {
|
||||
$self->EndModal(wxID_OK);
|
||||
$self->Destroy;
|
||||
});
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
1;
|
|
@ -5,12 +5,12 @@ use utf8;
|
|||
|
||||
use Slic3r::Print::State ':steps';
|
||||
use Wx qw(:misc :sizer :slider :statictext :keycode wxWHITE wxCB_READONLY);
|
||||
use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX EVT_CHOICE EVT_CHECKLISTBOX);
|
||||
use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN EVT_CHECKBOX EVT_CHOICE EVT_CHECKLISTBOX EVT_SIZE);
|
||||
use base qw(Wx::Panel Class::Accessor);
|
||||
|
||||
use Wx::Locale gettext => 'L';
|
||||
|
||||
__PACKAGE__->mk_accessors(qw(print gcode_preview_data enabled _loaded canvas slider_low slider_high single_layer));
|
||||
__PACKAGE__->mk_accessors(qw(print gcode_preview_data enabled _loaded canvas slider_low slider_high single_layer double_slider_sizer));
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
|
@ -27,47 +27,47 @@ sub new {
|
|||
Slic3r::GUI::_3DScene::enable_shader($canvas, 1);
|
||||
Slic3r::GUI::_3DScene::set_config($canvas, $config);
|
||||
$self->canvas($canvas);
|
||||
my $slider_low = Wx::Slider->new(
|
||||
$self, -1,
|
||||
0, # default
|
||||
0, # min
|
||||
# my $slider_low = Wx::Slider->new(
|
||||
# $self, -1,
|
||||
# 0, # default
|
||||
# 0, # min
|
||||
# we set max to a bogus non-zero value because the MSW implementation of wxSlider
|
||||
# will skip drawing the slider if max <= min:
|
||||
1, # max
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxVERTICAL | wxSL_INVERSE,
|
||||
);
|
||||
$self->slider_low($slider_low);
|
||||
my $slider_high = Wx::Slider->new(
|
||||
$self, -1,
|
||||
0, # default
|
||||
0, # min
|
||||
# 1, # max
|
||||
# wxDefaultPosition,
|
||||
# wxDefaultSize,
|
||||
# wxVERTICAL | wxSL_INVERSE,
|
||||
# );
|
||||
# $self->slider_low($slider_low);
|
||||
# my $slider_high = Wx::Slider->new(
|
||||
# $self, -1,
|
||||
# 0, # default
|
||||
# 0, # min
|
||||
# we set max to a bogus non-zero value because the MSW implementation of wxSlider
|
||||
# will skip drawing the slider if max <= min:
|
||||
1, # max
|
||||
wxDefaultPosition,
|
||||
wxDefaultSize,
|
||||
wxVERTICAL | wxSL_INVERSE,
|
||||
);
|
||||
$self->slider_high($slider_high);
|
||||
# 1, # max
|
||||
# wxDefaultPosition,
|
||||
# wxDefaultSize,
|
||||
# wxVERTICAL | wxSL_INVERSE,
|
||||
# );
|
||||
# $self->slider_high($slider_high);
|
||||
|
||||
my $z_label_low = $self->{z_label_low} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
[40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
$z_label_low->SetFont($Slic3r::GUI::small_font);
|
||||
my $z_label_high = $self->{z_label_high} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
[40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
$z_label_high->SetFont($Slic3r::GUI::small_font);
|
||||
# my $z_label_low = $self->{z_label_low} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
# $z_label_low->SetFont($Slic3r::GUI::small_font);
|
||||
# my $z_label_high = $self->{z_label_high} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
# $z_label_high->SetFont($Slic3r::GUI::small_font);
|
||||
|
||||
my $z_label_low_idx = $self->{z_label_low_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
[40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
$z_label_low_idx->SetFont($Slic3r::GUI::small_font);
|
||||
my $z_label_high_idx = $self->{z_label_high_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
[40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
$z_label_high_idx->SetFont($Slic3r::GUI::small_font);
|
||||
# my $z_label_low_idx = $self->{z_label_low_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
# $z_label_low_idx->SetFont($Slic3r::GUI::small_font);
|
||||
# my $z_label_high_idx = $self->{z_label_high_idx} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||
# [40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||
# $z_label_high_idx->SetFont($Slic3r::GUI::small_font);
|
||||
|
||||
$self->single_layer(0);
|
||||
my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, L("1 Layer"));
|
||||
# $self->single_layer(0);
|
||||
# my $checkbox_singlelayer = $self->{checkbox_singlelayer} = Wx::CheckBox->new($self, -1, L("1 Layer"));
|
||||
|
||||
my $label_view_type = $self->{label_view_type} = Wx::StaticText->new($self, -1, L("View"));
|
||||
|
||||
|
@ -103,25 +103,30 @@ sub new {
|
|||
.L("Custom");
|
||||
Slic3r::GUI::create_combochecklist($combochecklist_features, $feature_text, $feature_items, 1);
|
||||
|
||||
my $double_slider_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
Slic3r::GUI::create_double_slider($self, $double_slider_sizer, $self->canvas);
|
||||
$self->double_slider_sizer($double_slider_sizer);
|
||||
|
||||
my $checkbox_travel = $self->{checkbox_travel} = Wx::CheckBox->new($self, -1, L("Travel"));
|
||||
my $checkbox_retractions = $self->{checkbox_retractions} = Wx::CheckBox->new($self, -1, L("Retractions"));
|
||||
my $checkbox_unretractions = $self->{checkbox_unretractions} = Wx::CheckBox->new($self, -1, L("Unretractions"));
|
||||
my $checkbox_shells = $self->{checkbox_shells} = Wx::CheckBox->new($self, -1, L("Shells"));
|
||||
|
||||
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
my $vsizer_outer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$vsizer->Add($slider_low, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
$vsizer->Add($z_label_low_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
$vsizer->Add($z_label_low, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
$hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
||||
$vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$vsizer->Add($slider_high, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
$vsizer->Add($z_label_high_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
$vsizer->Add($z_label_high, 0, 0, 0);
|
||||
$hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
||||
$vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
$vsizer_outer->Add($checkbox_singlelayer, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5);
|
||||
# my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
# my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
# my $vsizer_outer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
# $vsizer->Add($slider_low, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
# $vsizer->Add($z_label_low_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
# $vsizer->Add($z_label_low, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
# $hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
||||
# $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
# $vsizer->Add($slider_high, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
# $vsizer->Add($z_label_high_idx, 0, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
# $vsizer->Add($z_label_high, 0, 0, 0);
|
||||
# $hsizer->Add($vsizer, 0, wxEXPAND, 0);
|
||||
# $vsizer_outer->Add($hsizer, 3, wxALIGN_CENTER_HORIZONTAL, 0);
|
||||
# $vsizer_outer->Add($double_slider_sizer, 3, wxEXPAND, 0);
|
||||
# $vsizer_outer->Add($checkbox_singlelayer, 0, wxTOP | wxALIGN_CENTER_HORIZONTAL, 5);
|
||||
|
||||
my $bottom_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$bottom_sizer->Add($label_view_type, 0, wxALIGN_CENTER_VERTICAL, 5);
|
||||
|
@ -140,81 +145,83 @@ sub new {
|
|||
|
||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$sizer->Add($canvas, 1, wxALL | wxEXPAND, 0);
|
||||
$sizer->Add($vsizer_outer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5);
|
||||
# $sizer->Add($vsizer_outer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5);
|
||||
$sizer->Add($double_slider_sizer, 0, wxEXPAND, 0);#wxTOP | wxBOTTOM | wxEXPAND, 5);
|
||||
|
||||
my $main_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$main_sizer->Add($sizer, 1, wxALL | wxEXPAND, 0);
|
||||
$main_sizer->Add($bottom_sizer, 0, wxALL | wxEXPAND, 0);
|
||||
|
||||
EVT_SLIDER($self, $slider_low, sub {
|
||||
$slider_high->SetValue($slider_low->GetValue) if $self->single_layer;
|
||||
$self->set_z_idx_low ($slider_low ->GetValue)
|
||||
});
|
||||
EVT_SLIDER($self, $slider_high, sub {
|
||||
$slider_low->SetValue($slider_high->GetValue) if $self->single_layer;
|
||||
$self->set_z_idx_high($slider_high->GetValue)
|
||||
});
|
||||
EVT_KEY_DOWN($canvas, sub {
|
||||
my ($s, $event) = @_;
|
||||
my $key = $event->GetKeyCode;
|
||||
if ($event->HasModifiers) {
|
||||
$event->Skip;
|
||||
} else {
|
||||
if ($key == ord('U')) {
|
||||
$slider_high->SetValue($slider_high->GetValue + 1);
|
||||
$slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown());
|
||||
$self->set_z_idx_high($slider_high->GetValue);
|
||||
} elsif ($key == ord('D')) {
|
||||
$slider_high->SetValue($slider_high->GetValue - 1);
|
||||
$slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown());
|
||||
$self->set_z_idx_high($slider_high->GetValue);
|
||||
} elsif ($key == ord('S')) {
|
||||
$checkbox_singlelayer->SetValue(! $checkbox_singlelayer->GetValue());
|
||||
$self->single_layer($checkbox_singlelayer->GetValue());
|
||||
if ($self->single_layer) {
|
||||
$slider_low->SetValue($slider_high->GetValue);
|
||||
$self->set_z_idx_high($slider_high->GetValue);
|
||||
}
|
||||
} else {
|
||||
$event->Skip;
|
||||
}
|
||||
}
|
||||
});
|
||||
EVT_KEY_DOWN($slider_low, sub {
|
||||
my ($s, $event) = @_;
|
||||
my $key = $event->GetKeyCode;
|
||||
if ($event->HasModifiers) {
|
||||
$event->Skip;
|
||||
} else {
|
||||
if ($key == WXK_LEFT) {
|
||||
} elsif ($key == WXK_RIGHT) {
|
||||
$slider_high->SetFocus;
|
||||
} else {
|
||||
$event->Skip;
|
||||
}
|
||||
}
|
||||
});
|
||||
EVT_KEY_DOWN($slider_high, sub {
|
||||
my ($s, $event) = @_;
|
||||
my $key = $event->GetKeyCode;
|
||||
if ($event->HasModifiers) {
|
||||
$event->Skip;
|
||||
} else {
|
||||
if ($key == WXK_LEFT) {
|
||||
$slider_low->SetFocus;
|
||||
} elsif ($key == WXK_RIGHT) {
|
||||
} else {
|
||||
$event->Skip;
|
||||
}
|
||||
}
|
||||
});
|
||||
EVT_CHECKBOX($self, $checkbox_singlelayer, sub {
|
||||
$self->single_layer($checkbox_singlelayer->GetValue());
|
||||
if ($self->single_layer) {
|
||||
$slider_low->SetValue($slider_high->GetValue);
|
||||
$self->set_z_idx_high($slider_high->GetValue);
|
||||
}
|
||||
});
|
||||
# EVT_SLIDER($self, $slider_low, sub {
|
||||
# $slider_high->SetValue($slider_low->GetValue) if $self->single_layer;
|
||||
# $self->set_z_idx_low ($slider_low ->GetValue)
|
||||
# });
|
||||
# EVT_SLIDER($self, $slider_high, sub {
|
||||
# $slider_low->SetValue($slider_high->GetValue) if $self->single_layer;
|
||||
# $self->set_z_idx_high($slider_high->GetValue)
|
||||
# });
|
||||
# EVT_KEY_DOWN($canvas, sub {
|
||||
# my ($s, $event) = @_;
|
||||
# Slic3r::GUI::update_double_slider_from_canvas($event);
|
||||
# my $key = $event->GetKeyCode;
|
||||
# if ($event->HasModifiers) {
|
||||
# $event->Skip;
|
||||
# } else {
|
||||
# if ($key == ord('U')) {
|
||||
# $slider_high->SetValue($slider_high->GetValue + 1);
|
||||
# $slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown());
|
||||
# $self->set_z_idx_high($slider_high->GetValue);
|
||||
# } elsif ($key == ord('D')) {
|
||||
# $slider_high->SetValue($slider_high->GetValue - 1);
|
||||
# $slider_low->SetValue($slider_high->GetValue) if ($event->ShiftDown());
|
||||
# $self->set_z_idx_high($slider_high->GetValue);
|
||||
# } elsif ($key == ord('S')) {
|
||||
# $checkbox_singlelayer->SetValue(! $checkbox_singlelayer->GetValue());
|
||||
# $self->single_layer($checkbox_singlelayer->GetValue());
|
||||
# if ($self->single_layer) {
|
||||
# $slider_low->SetValue($slider_high->GetValue);
|
||||
# $self->set_z_idx_high($slider_high->GetValue);
|
||||
# }
|
||||
# } else {
|
||||
# $event->Skip;
|
||||
# }
|
||||
# }
|
||||
# });
|
||||
# EVT_KEY_DOWN($slider_low, sub {
|
||||
# my ($s, $event) = @_;
|
||||
# my $key = $event->GetKeyCode;
|
||||
# if ($event->HasModifiers) {
|
||||
# $event->Skip;
|
||||
# } else {
|
||||
# if ($key == WXK_LEFT) {
|
||||
# } elsif ($key == WXK_RIGHT) {
|
||||
# $slider_high->SetFocus;
|
||||
# } else {
|
||||
# $event->Skip;
|
||||
# }
|
||||
# }
|
||||
# });
|
||||
# EVT_KEY_DOWN($slider_high, sub {
|
||||
# my ($s, $event) = @_;
|
||||
# my $key = $event->GetKeyCode;
|
||||
# if ($event->HasModifiers) {
|
||||
# $event->Skip;
|
||||
# } else {
|
||||
# if ($key == WXK_LEFT) {
|
||||
# $slider_low->SetFocus;
|
||||
# } elsif ($key == WXK_RIGHT) {
|
||||
# } else {
|
||||
# $event->Skip;
|
||||
# }
|
||||
# }
|
||||
# });
|
||||
# EVT_CHECKBOX($self, $checkbox_singlelayer, sub {
|
||||
# $self->single_layer($checkbox_singlelayer->GetValue());
|
||||
# if ($self->single_layer) {
|
||||
# $slider_low->SetValue($slider_high->GetValue);
|
||||
# $self->set_z_idx_high($slider_high->GetValue);
|
||||
# }
|
||||
# });
|
||||
EVT_CHOICE($self, $choice_view_type, sub {
|
||||
my $selection = $choice_view_type->GetCurrentSelection();
|
||||
$self->{preferred_color_mode} = ($selection == $self->{tool_idx}) ? 'tool' : 'feature';
|
||||
|
@ -244,6 +251,12 @@ sub new {
|
|||
$self->refresh_print;
|
||||
});
|
||||
|
||||
EVT_SIZE($self, sub {
|
||||
my ($s, $event) = @_;
|
||||
$event->Skip;
|
||||
$self->Refresh;
|
||||
});
|
||||
|
||||
$self->SetSizer($main_sizer);
|
||||
$self->SetMinSize($self->GetSize);
|
||||
$sizer->SetSizeHints($self);
|
||||
|
@ -392,62 +405,69 @@ sub load_print {
|
|||
sub reset_sliders {
|
||||
my ($self) = @_;
|
||||
$self->enabled(0);
|
||||
$self->set_z_range(0,0);
|
||||
$self->slider_low->Hide;
|
||||
$self->slider_high->Hide;
|
||||
$self->{z_label_low}->SetLabel("");
|
||||
$self->{z_label_high}->SetLabel("");
|
||||
$self->{z_label_low_idx}->SetLabel("");
|
||||
$self->{z_label_high_idx}->SetLabel("");
|
||||
# $self->set_z_range(0,0);
|
||||
# $self->slider_low->Hide;
|
||||
# $self->slider_high->Hide;
|
||||
# $self->{z_label_low}->SetLabel("");
|
||||
# $self->{z_label_high}->SetLabel("");
|
||||
# $self->{z_label_low_idx}->SetLabel("");
|
||||
# $self->{z_label_high_idx}->SetLabel("");
|
||||
|
||||
Slic3r::GUI::reset_double_slider();
|
||||
$self->double_slider_sizer->Hide(0);
|
||||
}
|
||||
|
||||
sub update_sliders
|
||||
{
|
||||
my ($self, $n_layers) = @_;
|
||||
|
||||
my $z_idx_low = $self->slider_low->GetValue;
|
||||
my $z_idx_high = $self->slider_high->GetValue;
|
||||
# my $z_idx_low = $self->slider_low->GetValue;
|
||||
# my $z_idx_high = $self->slider_high->GetValue;
|
||||
$self->enabled(1);
|
||||
$self->slider_low->SetRange(0, $n_layers - 1);
|
||||
$self->slider_high->SetRange(0, $n_layers - 1);
|
||||
# $self->slider_low->SetRange(0, $n_layers - 1);
|
||||
# $self->slider_high->SetRange(0, $n_layers - 1);
|
||||
|
||||
if ($self->{force_sliders_full_range}) {
|
||||
$z_idx_low = 0;
|
||||
$z_idx_high = $n_layers - 1;
|
||||
} elsif ($z_idx_high < $n_layers && ($self->single_layer || $z_idx_high != 0)) {
|
||||
# search new indices for nearest z (size of $self->{layers_z} may change in dependence of what is shown)
|
||||
if (defined($self->{z_low})) {
|
||||
for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
|
||||
if ($self->{layers_z}[$i] <= $self->{z_low}) {
|
||||
$z_idx_low = $i;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (defined($self->{z_high})) {
|
||||
for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
|
||||
if ($self->{layers_z}[$i] <= $self->{z_high}) {
|
||||
$z_idx_high = $i;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elsif ($z_idx_high >= $n_layers) {
|
||||
# Out of range. Disable 'single layer' view.
|
||||
$self->single_layer(0);
|
||||
$self->{checkbox_singlelayer}->SetValue(0);
|
||||
$z_idx_low = 0;
|
||||
$z_idx_high = $n_layers - 1;
|
||||
} else {
|
||||
$z_idx_low = 0;
|
||||
$z_idx_high = $n_layers - 1;
|
||||
}
|
||||
# if ($self->{force_sliders_full_range}) {
|
||||
# $z_idx_low = 0;
|
||||
# $z_idx_high = $n_layers - 1;
|
||||
# } elsif ($z_idx_high < $n_layers && ($self->single_layer || $z_idx_high != 0)) {
|
||||
# # search new indices for nearest z (size of $self->{layers_z} may change in dependence of what is shown)
|
||||
# if (defined($self->{z_low})) {
|
||||
# for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
|
||||
# if ($self->{layers_z}[$i] <= $self->{z_low}) {
|
||||
# $z_idx_low = $i;
|
||||
# last;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# if (defined($self->{z_high})) {
|
||||
# for (my $i = scalar(@{$self->{layers_z}}) - 1; $i >= 0; $i -= 1) {
|
||||
# if ($self->{layers_z}[$i] <= $self->{z_high}) {
|
||||
# $z_idx_high = $i;
|
||||
# last;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# } elsif ($z_idx_high >= $n_layers) {
|
||||
# # Out of range. Disable 'single layer' view.
|
||||
# $self->single_layer(0);
|
||||
# $self->{checkbox_singlelayer}->SetValue(0);
|
||||
# $z_idx_low = 0;
|
||||
# $z_idx_high = $n_layers - 1;
|
||||
# } else {
|
||||
# $z_idx_low = 0;
|
||||
# $z_idx_high = $n_layers - 1;
|
||||
# }
|
||||
|
||||
# $self->slider_low->SetValue($z_idx_low);
|
||||
# $self->slider_high->SetValue($z_idx_high);
|
||||
# $self->slider_low->Show;
|
||||
# $self->slider_high->Show;
|
||||
# $self->set_z_range($self->{layers_z}[$z_idx_low], $self->{layers_z}[$z_idx_high]);
|
||||
|
||||
Slic3r::GUI::update_double_slider($self->{force_sliders_full_range});
|
||||
$self->double_slider_sizer->Show(0);
|
||||
|
||||
$self->slider_low->SetValue($z_idx_low);
|
||||
$self->slider_high->SetValue($z_idx_high);
|
||||
$self->slider_low->Show;
|
||||
$self->slider_high->Show;
|
||||
$self->set_z_range($self->{layers_z}[$z_idx_low], $self->{layers_z}[$z_idx_high]);
|
||||
$self->Layout;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,221 +0,0 @@
|
|||
# Generate an anonymous or "lambda" 3D object. This gets used with the Add Generic option in Settings.
|
||||
#
|
||||
|
||||
package Slic3r::GUI::Plater::LambdaObjectDialog;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL wxCB_READONLY wxTE_PROCESS_TAB);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_BUTTON EVT_COMBOBOX EVT_TEXT);
|
||||
use Scalar::Util qw(looks_like_number);
|
||||
use base 'Wx::Dialog';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, %params) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, "Lambda Object", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||
# Note whether the window was already closed, so a pending update is not executed.
|
||||
$self->{already_closed} = 0;
|
||||
$self->{object_parameters} = {
|
||||
type => "box",
|
||||
dim => [1, 1, 1],
|
||||
cyl_r => 1,
|
||||
cyl_h => 1,
|
||||
sph_rho => 1.0,
|
||||
slab_h => 1.0,
|
||||
slab_z => 0.0,
|
||||
};
|
||||
|
||||
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||
my $button_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
my $button_ok = $self->CreateStdDialogButtonSizer(wxOK);
|
||||
my $button_cancel = $self->CreateStdDialogButtonSizer(wxCANCEL);
|
||||
$button_sizer->Add($button_ok);
|
||||
$button_sizer->Add($button_cancel);
|
||||
EVT_BUTTON($self, wxID_OK, sub {
|
||||
# validate user input
|
||||
return if !$self->CanClose;
|
||||
|
||||
$self->EndModal(wxID_OK);
|
||||
$self->Destroy;
|
||||
});
|
||||
EVT_BUTTON($self, wxID_CANCEL, sub {
|
||||
# validate user input
|
||||
return if !$self->CanClose;
|
||||
|
||||
$self->EndModal(wxID_CANCEL);
|
||||
$self->Destroy;
|
||||
});
|
||||
|
||||
my $optgroup_box;
|
||||
$optgroup_box = $self->{optgroup_box} = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Add Cube...',
|
||||
on_change => sub {
|
||||
# Do validation
|
||||
my ($opt_id) = @_;
|
||||
if ($opt_id == 0 || $opt_id == 1 || $opt_id == 2) {
|
||||
if (!looks_like_number($optgroup_box->get_value($opt_id))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
$self->{object_parameters}->{dim}[$opt_id] = $optgroup_box->get_value($opt_id);
|
||||
},
|
||||
label_width => 100,
|
||||
);
|
||||
my @options = ("box", "slab", "cylinder", "sphere");
|
||||
$self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY);
|
||||
|
||||
$optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 0,
|
||||
label => 'L',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
$optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 1,
|
||||
label => 'W',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
$optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 2,
|
||||
label => 'H',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
|
||||
my $optgroup_cylinder;
|
||||
$optgroup_cylinder = $self->{optgroup_cylinder} = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Add Cylinder...',
|
||||
on_change => sub {
|
||||
# Do validation
|
||||
my ($opt_id) = @_;
|
||||
if ($opt_id eq 'cyl_r' || $opt_id eq 'cyl_h') {
|
||||
if (!looks_like_number($optgroup_cylinder->get_value($opt_id))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
$self->{object_parameters}->{$opt_id} = $optgroup_cylinder->get_value($opt_id);
|
||||
},
|
||||
label_width => 100,
|
||||
);
|
||||
|
||||
$optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => "cyl_r",
|
||||
label => 'Radius',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
$optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => "cyl_h",
|
||||
label => 'Height',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
|
||||
my $optgroup_sphere;
|
||||
$optgroup_sphere = $self->{optgroup_sphere} = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Add Sphere...',
|
||||
on_change => sub {
|
||||
# Do validation
|
||||
my ($opt_id) = @_;
|
||||
if ($opt_id eq 'sph_rho') {
|
||||
if (!looks_like_number($optgroup_sphere->get_value($opt_id))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
$self->{object_parameters}->{$opt_id} = $optgroup_sphere->get_value($opt_id);
|
||||
},
|
||||
label_width => 100,
|
||||
);
|
||||
|
||||
$optgroup_sphere->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => "sph_rho",
|
||||
label => 'Rho',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
|
||||
my $optgroup_slab;
|
||||
$optgroup_slab = $self->{optgroup_slab} = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Add Slab...',
|
||||
on_change => sub {
|
||||
# Do validation
|
||||
my ($opt_id) = @_;
|
||||
if ($opt_id eq 'slab_z' || $opt_id eq 'slab_h') {
|
||||
if (!looks_like_number($optgroup_slab->get_value($opt_id))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
$self->{object_parameters}->{$opt_id} = $optgroup_slab->get_value($opt_id);
|
||||
},
|
||||
label_width => 100,
|
||||
);
|
||||
$optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => "slab_h",
|
||||
label => 'H',
|
||||
type => 'f',
|
||||
default => '1',
|
||||
));
|
||||
$optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => "slab_z",
|
||||
label => 'Initial Z',
|
||||
type => 'f',
|
||||
default => '0',
|
||||
));
|
||||
|
||||
|
||||
EVT_COMBOBOX($self, 1, sub{
|
||||
$self->{object_parameters}->{type} = $self->{type}->GetValue();
|
||||
$self->_update_ui;
|
||||
});
|
||||
|
||||
|
||||
$self->{sizer}->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->{sizer}->Add($optgroup_box->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->{sizer}->Add($optgroup_cylinder->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->{sizer}->Add($optgroup_sphere->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->{sizer}->Add($optgroup_slab->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
$self->_update_ui;
|
||||
|
||||
$self->SetSizer($self->{sizer});
|
||||
$self->{sizer}->Fit($self);
|
||||
$self->{sizer}->SetSizeHints($self);
|
||||
|
||||
|
||||
return $self;
|
||||
}
|
||||
sub CanClose {
|
||||
return 1;
|
||||
}
|
||||
sub ObjectParameter {
|
||||
my ($self) = @_;
|
||||
return $self->{object_parameters};
|
||||
}
|
||||
|
||||
sub _update_ui {
|
||||
my ($self) = @_;
|
||||
$self->{sizer}->Hide($self->{optgroup_cylinder}->sizer);
|
||||
$self->{sizer}->Hide($self->{optgroup_slab}->sizer);
|
||||
$self->{sizer}->Hide($self->{optgroup_box}->sizer);
|
||||
$self->{sizer}->Hide($self->{optgroup_sphere}->sizer);
|
||||
if ($self->{type}->GetValue eq "box") {
|
||||
$self->{sizer}->Show($self->{optgroup_box}->sizer);
|
||||
} elsif ($self->{type}->GetValue eq "cylinder") {
|
||||
$self->{sizer}->Show($self->{optgroup_cylinder}->sizer);
|
||||
} elsif ($self->{type}->GetValue eq "slab") {
|
||||
$self->{sizer}->Show($self->{optgroup_slab}->sizer);
|
||||
} elsif ($self->{type}->GetValue eq "sphere") {
|
||||
$self->{sizer}->Show($self->{optgroup_sphere}->sizer);
|
||||
}
|
||||
$self->{sizer}->Fit($self);
|
||||
$self->{sizer}->SetSizeHints($self);
|
||||
|
||||
}
|
||||
1;
|
|
@ -1,284 +0,0 @@
|
|||
# Cut an object at a Z position, keep either the top or the bottom of the object.
|
||||
# This dialog gets opened with the "Cut..." button above the platter.
|
||||
|
||||
package Slic3r::GUI::Plater::ObjectCutDialog;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Slic3r::Geometry qw(PI X);
|
||||
use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL);
|
||||
use Wx::Event qw(EVT_CLOSE EVT_BUTTON);
|
||||
use List::Util qw(max);
|
||||
use base 'Wx::Dialog';
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, %params) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||
$self->{model_object} = $params{model_object};
|
||||
$self->{new_model_objects} = [];
|
||||
# Mark whether the mesh cut is valid.
|
||||
# If not, it needs to be recalculated by _update() on wxTheApp->CallAfter() or on exit of the dialog.
|
||||
$self->{mesh_cut_valid} = 0;
|
||||
# Note whether the window was already closed, so a pending update is not executed.
|
||||
$self->{already_closed} = 0;
|
||||
|
||||
# cut options
|
||||
$self->{cut_options} = {
|
||||
z => 0,
|
||||
keep_upper => 1,
|
||||
keep_lower => 1,
|
||||
rotate_lower => 1,
|
||||
# preview => 1,
|
||||
# Disabled live preview by default as it is not stable and/or the calculation takes too long for interactive usage.
|
||||
preview => 0,
|
||||
};
|
||||
|
||||
my $optgroup;
|
||||
$optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Cut',
|
||||
on_change => sub {
|
||||
my ($opt_id) = @_;
|
||||
# There seems to be an issue with wxWidgets 3.0.2/3.0.3, where the slider
|
||||
# genates tens of events for a single value change.
|
||||
# Only trigger the recalculation if the value changes
|
||||
# or a live preview was activated and the mesh cut is not valid yet.
|
||||
if ($self->{cut_options}{$opt_id} != $optgroup->get_value($opt_id) ||
|
||||
! $self->{mesh_cut_valid} && $self->_life_preview_active()) {
|
||||
$self->{cut_options}{$opt_id} = $optgroup->get_value($opt_id);
|
||||
$self->{mesh_cut_valid} = 0;
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_update;
|
||||
});
|
||||
}
|
||||
},
|
||||
label_width => 120,
|
||||
);
|
||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'z',
|
||||
type => 'slider',
|
||||
label => 'Z',
|
||||
default => $self->{cut_options}{z},
|
||||
min => 0,
|
||||
max => $self->{model_object}->bounding_box->size->z,
|
||||
full_width => 1,
|
||||
));
|
||||
{
|
||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
||||
label => 'Keep',
|
||||
);
|
||||
$line->append_option(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'keep_upper',
|
||||
type => 'bool',
|
||||
label => 'Upper part',
|
||||
default => $self->{cut_options}{keep_upper},
|
||||
));
|
||||
$line->append_option(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'keep_lower',
|
||||
type => 'bool',
|
||||
label => 'Lower part',
|
||||
default => $self->{cut_options}{keep_lower},
|
||||
));
|
||||
$optgroup->append_line($line);
|
||||
}
|
||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'rotate_lower',
|
||||
label => 'Rotate lower part upwards',
|
||||
type => 'bool',
|
||||
tooltip => 'If enabled, the lower part will be rotated by 180° so that the flat cut surface lies on the print bed.',
|
||||
default => $self->{cut_options}{rotate_lower},
|
||||
));
|
||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'preview',
|
||||
label => 'Show preview',
|
||||
type => 'bool',
|
||||
tooltip => 'If enabled, object will be cut in real time.',
|
||||
default => $self->{cut_options}{preview},
|
||||
));
|
||||
{
|
||||
my $cut_button_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$self->{btn_cut} = Wx::Button->new($self, -1, "Perform cut", wxDefaultPosition, wxDefaultSize);
|
||||
$cut_button_sizer->Add($self->{btn_cut}, 0, wxALIGN_RIGHT | wxALL, 10);
|
||||
$optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new(
|
||||
sizer => $cut_button_sizer,
|
||||
));
|
||||
}
|
||||
|
||||
# left pane with tree
|
||||
my $left_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$left_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
|
||||
# right pane with preview canvas
|
||||
my $canvas;
|
||||
if ($Slic3r::GUI::have_OpenGL) {
|
||||
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
|
||||
Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]);
|
||||
Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas);
|
||||
Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size }));
|
||||
$canvas->SetSize([500,500]);
|
||||
$canvas->SetMinSize($canvas->GetSize);
|
||||
Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->{config});
|
||||
Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1);
|
||||
}
|
||||
|
||||
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
|
||||
$self->{sizer}->Add($canvas, 1, wxEXPAND | wxALL, 0) if $canvas;
|
||||
|
||||
$self->SetSizer($self->{sizer});
|
||||
$self->SetMinSize($self->GetSize);
|
||||
$self->{sizer}->SetSizeHints($self);
|
||||
|
||||
EVT_BUTTON($self, $self->{btn_cut}, sub {
|
||||
# Recalculate the cut if the preview was not active.
|
||||
$self->_perform_cut() unless $self->{mesh_cut_valid};
|
||||
|
||||
# Adjust position / orientation of the split object halves.
|
||||
if ($self->{new_model_objects}{lower}) {
|
||||
if ($self->{cut_options}{rotate_lower}) {
|
||||
$self->{new_model_objects}{lower}->rotate(PI, Slic3r::Pointf3->new(1,0,0));
|
||||
$self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0
|
||||
}
|
||||
}
|
||||
if ($self->{new_model_objects}{upper}) {
|
||||
$self->{new_model_objects}{upper}->center_around_origin; # align to Z = 0
|
||||
}
|
||||
|
||||
# Note that the window was already closed, so a pending update will not be executed.
|
||||
$self->{already_closed} = 1;
|
||||
$self->EndModal(wxID_OK);
|
||||
$self->{canvas}->Destroy;
|
||||
$self->Destroy();
|
||||
});
|
||||
|
||||
EVT_CLOSE($self, sub {
|
||||
# Note that the window was already closed, so a pending update will not be executed.
|
||||
$self->{already_closed} = 1;
|
||||
$self->EndModal(wxID_CANCEL);
|
||||
$self->{canvas}->Destroy;
|
||||
$self->Destroy();
|
||||
});
|
||||
|
||||
$self->_update;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
# scale Z down to original size since we're using the transformed mesh for 3D preview
|
||||
# and cut dialog but ModelObject::cut() needs Z without any instance transformation
|
||||
sub _mesh_slice_z_pos
|
||||
{
|
||||
my ($self) = @_;
|
||||
return $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor;
|
||||
}
|
||||
|
||||
# Only perform live preview if just a single part of the object shall survive.
|
||||
sub _life_preview_active
|
||||
{
|
||||
my ($self) = @_;
|
||||
return $self->{cut_options}{preview} && ($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower});
|
||||
}
|
||||
|
||||
# Slice the mesh, keep the top / bottom part.
|
||||
sub _perform_cut
|
||||
{
|
||||
my ($self) = @_;
|
||||
|
||||
# Early exit. If the cut is valid, don't recalculate it.
|
||||
return if $self->{mesh_cut_valid};
|
||||
|
||||
my $z = $self->_mesh_slice_z_pos();
|
||||
|
||||
my ($new_model) = $self->{model_object}->cut($z);
|
||||
my ($upper_object, $lower_object) = @{$new_model->objects};
|
||||
$self->{new_model} = $new_model;
|
||||
$self->{new_model_objects} = {};
|
||||
if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) {
|
||||
$self->{new_model_objects}{upper} = $upper_object;
|
||||
}
|
||||
if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) {
|
||||
$self->{new_model_objects}{lower} = $lower_object;
|
||||
}
|
||||
|
||||
$self->{mesh_cut_valid} = 1;
|
||||
}
|
||||
|
||||
sub _update {
|
||||
my ($self) = @_;
|
||||
|
||||
# Don't update if the window was already closed.
|
||||
# We are not sure whether the action planned by wxTheApp->CallAfter() may be triggered after the window is closed.
|
||||
# Probably not, but better be safe than sorry, which is espetially true on multiple platforms.
|
||||
return if $self->{already_closed};
|
||||
|
||||
# Only recalculate the cut, if the live cut preview is active.
|
||||
my $life_preview_active = $self->_life_preview_active();
|
||||
$self->_perform_cut() if $life_preview_active;
|
||||
|
||||
{
|
||||
# scale Z down to original size since we're using the transformed mesh for 3D preview
|
||||
# and cut dialog but ModelObject::cut() needs Z without any instance transformation
|
||||
my $z = $self->_mesh_slice_z_pos();
|
||||
|
||||
|
||||
# update canvas
|
||||
if ($self->{canvas}) {
|
||||
# get volumes to render
|
||||
my @objects = ();
|
||||
if ($life_preview_active) {
|
||||
push @objects, values %{$self->{new_model_objects}};
|
||||
} else {
|
||||
push @objects, $self->{model_object};
|
||||
}
|
||||
|
||||
my $z_cut = $z + $self->{model_object}->bounding_box->z_min;
|
||||
|
||||
# get section contour
|
||||
my @expolygons = ();
|
||||
foreach my $volume (@{$self->{model_object}->volumes}) {
|
||||
next if !$volume->mesh;
|
||||
next if !$volume->model_part;
|
||||
my $expp = $volume->mesh->slice([ $z_cut ])->[0];
|
||||
push @expolygons, @$expp;
|
||||
}
|
||||
foreach my $expolygon (@expolygons) {
|
||||
$self->{model_object}->instances->[0]->transform_polygon($_)
|
||||
for @$expolygon;
|
||||
$expolygon->translate(map Slic3r::Geometry::scale($_), @{ $self->{model_object}->instances->[0]->offset });
|
||||
}
|
||||
|
||||
Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects;
|
||||
Slic3r::GUI::_3DScene::set_cutting_plane($self->{canvas}, $self->{cut_options}{z}, [@expolygons]);
|
||||
Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::render($self->{canvas});
|
||||
}
|
||||
}
|
||||
|
||||
# update controls
|
||||
{
|
||||
my $z = $self->{cut_options}{z};
|
||||
my $optgroup = $self->{optgroup};
|
||||
$optgroup->get_field('keep_upper')->toggle(my $have_upper = abs($z - $optgroup->get_option('z')->max) > 0.1);
|
||||
$optgroup->get_field('keep_lower')->toggle(my $have_lower = $z > 0.1);
|
||||
$optgroup->get_field('rotate_lower')->toggle($z > 0 && $self->{cut_options}{keep_lower});
|
||||
# Disabled live preview by default as it is not stable and/or the calculation takes too long for interactive usage.
|
||||
# $optgroup->get_field('preview')->toggle($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower});
|
||||
|
||||
# update cut button
|
||||
if (($self->{cut_options}{keep_upper} && $have_upper)
|
||||
|| ($self->{cut_options}{keep_lower} && $have_lower)) {
|
||||
$self->{btn_cut}->Enable;
|
||||
} else {
|
||||
$self->{btn_cut}->Disable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub NewModelObjects {
|
||||
my ($self) = @_;
|
||||
return values %{ $self->{new_model_objects} };
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,645 +0,0 @@
|
|||
# Configuration of mesh modifiers and their parameters.
|
||||
# This panel is inserted into ObjectSettingsDialog.
|
||||
|
||||
package Slic3r::GUI::Plater::ObjectPartsPanel;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use File::Basename qw(basename);
|
||||
use Wx qw(:misc :sizer :treectrl :button :keycode wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL wxMOD_CONTROL
|
||||
wxTheApp);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_KEY_DOWN EVT_KEY_DOWN);
|
||||
use List::Util qw(max);
|
||||
use base 'Wx::Panel';
|
||||
|
||||
use constant ICON_OBJECT => 0;
|
||||
use constant ICON_SOLIDMESH => 1;
|
||||
use constant ICON_MODIFIERMESH => 2;
|
||||
use constant ICON_SUPPORT_ENFORCER => 3;
|
||||
use constant ICON_SUPPORT_BLOCKER => 4;
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, %params) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
|
||||
# C++ type Slic3r::ModelObject
|
||||
my $object = $self->{model_object} = $params{model_object};
|
||||
|
||||
# Save state for sliders.
|
||||
$self->{move_options} = {
|
||||
x => 0,
|
||||
y => 0,
|
||||
z => 0,
|
||||
};
|
||||
$self->{last_coords} = {
|
||||
x => 0,
|
||||
y => 0,
|
||||
z => 0,
|
||||
};
|
||||
|
||||
# create TreeCtrl
|
||||
my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100],
|
||||
wxTR_NO_BUTTONS | wxSUNKEN_BORDER | wxTR_HAS_VARIABLE_ROW_HEIGHT
|
||||
| wxTR_SINGLE);
|
||||
{
|
||||
$self->{tree_icons} = Wx::ImageList->new(16, 16, 1);
|
||||
$tree->AssignImageList($self->{tree_icons});
|
||||
$self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("brick.png"), wxBITMAP_TYPE_PNG)); # ICON_OBJECT
|
||||
$self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("package.png"), wxBITMAP_TYPE_PNG)); # ICON_SOLIDMESH
|
||||
$self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("plugin.png"), wxBITMAP_TYPE_PNG)); # ICON_MODIFIERMESH
|
||||
$self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_enforcer.png"), wxBITMAP_TYPE_PNG)); # ICON_SUPPORT_ENFORCER
|
||||
$self->{tree_icons}->Add(Wx::Bitmap->new(Slic3r::var("support_blocker.png"), wxBITMAP_TYPE_PNG)); # ICON_SUPPORT_BLOCKER
|
||||
|
||||
my $rootId = $tree->AddRoot("Object", ICON_OBJECT);
|
||||
$tree->SetPlData($rootId, { type => 'object' });
|
||||
}
|
||||
|
||||
# buttons
|
||||
$self->{btn_load_part} = Wx::Button->new($self, -1, "Load part…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||
$self->{btn_load_modifier} = Wx::Button->new($self, -1, "Load modifier…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||
$self->{btn_load_lambda_modifier} = Wx::Button->new($self, -1, "Load generic…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||
$self->{btn_delete} = Wx::Button->new($self, -1, "Delete part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||
$self->{btn_split} = Wx::Button->new($self, -1, "Split part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||
$self->{btn_move_up} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT);
|
||||
$self->{btn_move_down} = Wx::Button->new($self, -1, "", wxDefaultPosition, [40, -1], wxBU_LEFT);
|
||||
$self->{btn_load_part}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG));
|
||||
$self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG));
|
||||
$self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_add.png"), wxBITMAP_TYPE_PNG));
|
||||
$self->{btn_delete}->SetBitmap(Wx::Bitmap->new(Slic3r::var("brick_delete.png"), wxBITMAP_TYPE_PNG));
|
||||
$self->{btn_split}->SetBitmap(Wx::Bitmap->new(Slic3r::var("shape_ungroup.png"), wxBITMAP_TYPE_PNG));
|
||||
$self->{btn_move_up}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_up.png"), wxBITMAP_TYPE_PNG));
|
||||
$self->{btn_move_down}->SetBitmap(Wx::Bitmap->new(Slic3r::var("bullet_arrow_down.png"), wxBITMAP_TYPE_PNG));
|
||||
|
||||
# buttons sizer
|
||||
my $buttons_sizer = Wx::GridSizer->new(2, 3);
|
||||
$buttons_sizer->Add($self->{btn_load_part}, 0, wxEXPAND | wxBOTTOM | wxRIGHT, 5);
|
||||
$buttons_sizer->Add($self->{btn_load_modifier}, 0, wxEXPAND | wxBOTTOM | wxRIGHT, 5);
|
||||
$buttons_sizer->Add($self->{btn_load_lambda_modifier}, 0, wxEXPAND | wxBOTTOM, 5);
|
||||
$buttons_sizer->Add($self->{btn_delete}, 0, wxEXPAND | wxRIGHT, 5);
|
||||
$buttons_sizer->Add($self->{btn_split}, 0, wxEXPAND | wxRIGHT, 5);
|
||||
{
|
||||
my $up_down_sizer = Wx::GridSizer->new(1, 2);
|
||||
$up_down_sizer->Add($self->{btn_move_up}, 0, wxEXPAND | wxRIGHT, 5);
|
||||
$up_down_sizer->Add($self->{btn_move_down}, 0, wxEXPAND, 5);
|
||||
$buttons_sizer->Add($up_down_sizer, 0, wxEXPAND, 5);
|
||||
}
|
||||
$self->{btn_load_part}->SetFont($Slic3r::GUI::small_font);
|
||||
$self->{btn_load_modifier}->SetFont($Slic3r::GUI::small_font);
|
||||
$self->{btn_load_lambda_modifier}->SetFont($Slic3r::GUI::small_font);
|
||||
$self->{btn_delete}->SetFont($Slic3r::GUI::small_font);
|
||||
$self->{btn_split}->SetFont($Slic3r::GUI::small_font);
|
||||
$self->{btn_move_up}->SetFont($Slic3r::GUI::small_font);
|
||||
$self->{btn_move_down}->SetFont($Slic3r::GUI::small_font);
|
||||
|
||||
# part settings panel
|
||||
$self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub {
|
||||
my ($key, $value) = @_;
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->set_part_type($value) if ($key eq "part_type");
|
||||
$self->{part_settings_changed} = 1;
|
||||
$self->_update_canvas;
|
||||
});
|
||||
});
|
||||
my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL);
|
||||
$settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0);
|
||||
|
||||
my $optgroup_movers;
|
||||
$optgroup_movers = $self->{optgroup_movers} = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Move',
|
||||
on_change => sub {
|
||||
my ($opt_id) = @_;
|
||||
# There seems to be an issue with wxWidgets 3.0.2/3.0.3, where the slider
|
||||
# genates tens of events for a single value change.
|
||||
# Only trigger the recalculation if the value changes
|
||||
# or a live preview was activated and the mesh cut is not valid yet.
|
||||
if ($self->{move_options}{$opt_id} != $optgroup_movers->get_value($opt_id)) {
|
||||
$self->{move_options}{$opt_id} = $optgroup_movers->get_value($opt_id);
|
||||
wxTheApp->CallAfter(sub {
|
||||
$self->_update;
|
||||
});
|
||||
}
|
||||
},
|
||||
label_width => 20,
|
||||
);
|
||||
$optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'x',
|
||||
type => 'slider',
|
||||
label => 'X',
|
||||
default => 0,
|
||||
min => -($self->{model_object}->bounding_box->size->x)*4,
|
||||
max => $self->{model_object}->bounding_box->size->x*4,
|
||||
full_width => 1,
|
||||
));
|
||||
$optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'y',
|
||||
type => 'slider',
|
||||
label => 'Y',
|
||||
default => 0,
|
||||
min => -($self->{model_object}->bounding_box->size->y)*4,
|
||||
max => $self->{model_object}->bounding_box->size->y*4,
|
||||
full_width => 1,
|
||||
));
|
||||
$optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
|
||||
opt_id => 'z',
|
||||
type => 'slider',
|
||||
label => 'Z',
|
||||
default => 0,
|
||||
min => -($self->{model_object}->bounding_box->size->z)*4,
|
||||
max => $self->{model_object}->bounding_box->size->z*4,
|
||||
full_width => 1,
|
||||
));
|
||||
|
||||
# left pane with tree
|
||||
my $left_sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$left_sizer->Add($tree, 3, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
$left_sizer->Add($buttons_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10);
|
||||
$left_sizer->Add($settings_sizer, 5, wxEXPAND | wxALL, 0);
|
||||
$left_sizer->Add($optgroup_movers->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
|
||||
# right pane with preview canvas
|
||||
my $canvas;
|
||||
if ($Slic3r::GUI::have_OpenGL) {
|
||||
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
|
||||
Slic3r::GUI::_3DScene::enable_picking($canvas, 1);
|
||||
Slic3r::GUI::_3DScene::set_select_by($canvas, 'volume');
|
||||
Slic3r::GUI::_3DScene::register_on_select_object_callback($canvas, sub {
|
||||
my ($volume_idx) = @_;
|
||||
$self->reload_tree($volume_idx);
|
||||
});
|
||||
Slic3r::GUI::_3DScene::load_model_object($canvas, $self->{model_object}, 0, [0]);
|
||||
Slic3r::GUI::_3DScene::set_auto_bed_shape($canvas);
|
||||
Slic3r::GUI::_3DScene::set_axes_length($canvas, 2.0 * max(@{ Slic3r::GUI::_3DScene::get_volumes_bounding_box($canvas)->size }));
|
||||
$canvas->SetSize([500,700]);
|
||||
Slic3r::GUI::_3DScene::set_config($canvas, $self->GetParent->GetParent->GetParent->{config});
|
||||
Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($canvas);
|
||||
Slic3r::GUI::_3DScene::enable_force_zoom_to_bed($canvas, 1);
|
||||
}
|
||||
|
||||
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxALL, 0);
|
||||
$self->{sizer}->Add($canvas, 1, wxEXPAND | wxALL, 0) if $canvas;
|
||||
|
||||
$self->SetSizer($self->{sizer});
|
||||
$self->{sizer}->SetSizeHints($self);
|
||||
|
||||
# attach events
|
||||
EVT_TREE_ITEM_COLLAPSING($self, $tree, sub {
|
||||
my ($self, $event) = @_;
|
||||
$event->Veto;
|
||||
});
|
||||
EVT_TREE_SEL_CHANGED($self, $tree, sub {
|
||||
my ($self, $event) = @_;
|
||||
return if $self->{disable_tree_sel_changed_event};
|
||||
$self->selection_changed;
|
||||
});
|
||||
EVT_TREE_KEY_DOWN($self, $tree, \&on_tree_key_down);
|
||||
EVT_BUTTON($self, $self->{btn_load_part}, sub { $self->on_btn_load(0) });
|
||||
EVT_BUTTON($self, $self->{btn_load_modifier}, sub { $self->on_btn_load(1) });
|
||||
EVT_BUTTON($self, $self->{btn_load_lambda_modifier}, sub { $self->on_btn_lambda(1) });
|
||||
EVT_BUTTON($self, $self->{btn_delete}, \&on_btn_delete);
|
||||
EVT_BUTTON($self, $self->{btn_split}, \&on_btn_split);
|
||||
EVT_BUTTON($self, $self->{btn_move_up}, \&on_btn_move_up);
|
||||
EVT_BUTTON($self, $self->{btn_move_down}, \&on_btn_move_down);
|
||||
EVT_KEY_DOWN($canvas, sub {
|
||||
my ($canvas, $event) = @_;
|
||||
if ($event->GetKeyCode == WXK_DELETE) {
|
||||
$canvas->GetParent->on_btn_delete;
|
||||
} else {
|
||||
$event->Skip;
|
||||
}
|
||||
});
|
||||
|
||||
$self->reload_tree;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub reload_tree {
|
||||
my ($self, $selected_volume_idx) = @_;
|
||||
|
||||
$selected_volume_idx //= -1;
|
||||
my $object = $self->{model_object};
|
||||
my $tree = $self->{tree};
|
||||
my $rootId = $tree->GetRootItem;
|
||||
|
||||
# despite wxWidgets states that DeleteChildren "will not generate any events unlike Delete() method",
|
||||
# the MSW implementation of DeleteChildren actually calls Delete() for each item, so
|
||||
# EVT_TREE_SEL_CHANGED is being called, with bad effects (the event handler is called; this
|
||||
# subroutine is never continued; an invisible EndModal is called on the dialog causing Plater
|
||||
# to continue its logic and rescheduling the background process etc. GH #2774)
|
||||
$self->{disable_tree_sel_changed_event} = 1;
|
||||
$tree->DeleteChildren($rootId);
|
||||
$self->{disable_tree_sel_changed_event} = 0;
|
||||
|
||||
my $selectedId = $rootId;
|
||||
foreach my $volume_id (0..$#{$object->volumes}) {
|
||||
my $volume = $object->volumes->[$volume_id];
|
||||
my $icon =
|
||||
$volume->modifier ? ICON_MODIFIERMESH :
|
||||
$volume->support_enforcer ? ICON_SUPPORT_ENFORCER :
|
||||
$volume->support_blocker ? ICON_SUPPORT_BLOCKER :
|
||||
ICON_SOLIDMESH;
|
||||
my $itemId = $tree->AppendItem($rootId, $volume->name || $volume_id, $icon);
|
||||
if ($volume_id == $selected_volume_idx) {
|
||||
$selectedId = $itemId;
|
||||
}
|
||||
$tree->SetPlData($itemId, {
|
||||
type => 'volume',
|
||||
volume_id => $volume_id,
|
||||
});
|
||||
}
|
||||
$tree->ExpandAll;
|
||||
|
||||
Slic3r::GUI->CallAfter(sub {
|
||||
$self->{tree}->SelectItem($selectedId);
|
||||
|
||||
# SelectItem() should trigger EVT_TREE_SEL_CHANGED as per wxWidgets docs,
|
||||
# but in fact it doesn't if the given item is already selected (this happens
|
||||
# on first load)
|
||||
$self->selection_changed;
|
||||
});
|
||||
}
|
||||
|
||||
sub get_selection {
|
||||
my ($self) = @_;
|
||||
|
||||
my $nodeId = $self->{tree}->GetSelection;
|
||||
if ($nodeId->IsOk) {
|
||||
return $self->{tree}->GetPlData($nodeId);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub selection_changed {
|
||||
my ($self) = @_;
|
||||
|
||||
# deselect all meshes
|
||||
if ($self->{canvas}) {
|
||||
Slic3r::GUI::_3DScene::deselect_volumes($self->{canvas});
|
||||
}
|
||||
|
||||
# disable things as if nothing is selected
|
||||
$self->{'btn_' . $_}->Disable for (qw(delete move_up move_down split));
|
||||
$self->{settings_panel}->disable;
|
||||
$self->{settings_panel}->set_config(undef);
|
||||
|
||||
# reset move sliders
|
||||
$self->{optgroup_movers}->set_value("x", 0);
|
||||
$self->{optgroup_movers}->set_value("y", 0);
|
||||
$self->{optgroup_movers}->set_value("z", 0);
|
||||
$self->{move_options} = {
|
||||
x => 0,
|
||||
y => 0,
|
||||
z => 0,
|
||||
};
|
||||
$self->{last_coords} = {
|
||||
x => 0,
|
||||
y => 0,
|
||||
z => 0,
|
||||
};
|
||||
|
||||
if (my $itemData = $self->get_selection) {
|
||||
my ($config, @opt_keys);
|
||||
my $type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_OBJECT;
|
||||
my $support = 0;
|
||||
if ($itemData->{type} eq 'volume') {
|
||||
# select volume in 3D preview
|
||||
if ($self->{canvas}) {
|
||||
Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id});
|
||||
}
|
||||
$self->{btn_delete}->Enable;
|
||||
$self->{btn_split}->Enable;
|
||||
$self->{btn_move_up}->Enable if $itemData->{volume_id} > 0;
|
||||
$self->{btn_move_down}->Enable if $itemData->{volume_id} + 1 < $self->{model_object}->volumes_count;
|
||||
|
||||
# attach volume config to settings panel
|
||||
my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ];
|
||||
|
||||
if (! $volume->model_part) {
|
||||
$self->{optgroup_movers}->enable;
|
||||
if ($volume->support_enforcer || $volume->support_blocker) {
|
||||
$support = 1;
|
||||
$type = $volume->support_enforcer ?
|
||||
Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER :
|
||||
Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER;
|
||||
} else {
|
||||
$type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER;
|
||||
}
|
||||
} else {
|
||||
$type = Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART;
|
||||
$self->{optgroup_movers}->disable;
|
||||
}
|
||||
$config = $volume->config;
|
||||
$self->{staticbox}->SetLabel('Part Settings');
|
||||
# get default values
|
||||
@opt_keys = $support ? () : @{Slic3r::Config::PrintRegion->new->get_keys};
|
||||
} elsif ($itemData->{type} eq 'object') {
|
||||
# select nothing in 3D preview
|
||||
|
||||
# attach object config to settings panel
|
||||
$self->{optgroup_movers}->disable;
|
||||
$self->{staticbox}->SetLabel('Object Settings');
|
||||
@opt_keys = (map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new);
|
||||
$config = $self->{model_object}->config;
|
||||
}
|
||||
# get default values
|
||||
my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
|
||||
|
||||
# decide which settings will be shown by default
|
||||
if ($itemData->{type} eq 'object') {
|
||||
$config->set_ifndef('wipe_into_objects', 0);
|
||||
$config->set_ifndef('wipe_into_infill', 0);
|
||||
}
|
||||
|
||||
# append default extruder
|
||||
if (! $support) {
|
||||
push @opt_keys, 'extruder';
|
||||
$default_config->set('extruder', 0);
|
||||
$config->set_ifndef('extruder', 0);
|
||||
}
|
||||
$self->{settings_panel}->set_type($type);
|
||||
$self->{settings_panel}->set_default_config($default_config);
|
||||
$self->{settings_panel}->set_config($config);
|
||||
$self->{settings_panel}->set_opt_keys(\@opt_keys);
|
||||
|
||||
# disable minus icon to remove the settings
|
||||
my $fixed_options =
|
||||
($itemData->{type} eq 'object') ? [qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)] :
|
||||
$support ? [] : [qw(extruder)];
|
||||
$self->{settings_panel}->set_fixed_options($fixed_options);
|
||||
$self->{settings_panel}->enable;
|
||||
}
|
||||
|
||||
Slic3r::GUI::_3DScene::render($self->{canvas}) if $self->{canvas};
|
||||
}
|
||||
|
||||
sub set_part_type
|
||||
{
|
||||
my ($self, $part_type) = @_;
|
||||
if (my $itemData = $self->get_selection) {
|
||||
if ($itemData->{type} eq 'volume') {
|
||||
my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ];
|
||||
if ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER ||
|
||||
$part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_PART) {
|
||||
$volume->set_modifier($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_MODIFIER);
|
||||
} elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_ENFORCER) {
|
||||
$volume->set_support_enforcer;
|
||||
} elsif ($part_type == Slic3r::GUI::Plater::OverrideSettingsPanel->TYPE_SUPPORT_BLOCKER) {
|
||||
$volume->set_support_blocker;
|
||||
}
|
||||
# We want the icon of the selected item to be changed as well.
|
||||
$self->reload_tree($itemData->{volume_id});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub on_btn_load {
|
||||
my ($self, $is_modifier) = @_;
|
||||
|
||||
my @input_files = wxTheApp->open_model($self);
|
||||
foreach my $input_file (@input_files) {
|
||||
my $model = eval { Slic3r::Model->read_from_file($input_file) };
|
||||
if ($@) {
|
||||
Slic3r::GUI::show_error($self, $@);
|
||||
next;
|
||||
}
|
||||
|
||||
foreach my $object (@{$model->objects}) {
|
||||
my $delta_x = 0.0;
|
||||
my $delta_y = 0.0;
|
||||
my $delta_z = 0.0;
|
||||
if (($self->{model_object}->origin_translation->x != 0.0) || ($self->{model_object}->origin_translation->y != 0.0) || ($self->{model_object}->origin_translation->z != 0.0)) {
|
||||
$object->center_around_origin;
|
||||
$delta_x = $self->{model_object}->origin_translation->x - $object->origin_translation->x;
|
||||
$delta_y = $self->{model_object}->origin_translation->y - $object->origin_translation->y;
|
||||
$delta_z = $self->{model_object}->origin_translation->z - $object->origin_translation->z;
|
||||
}
|
||||
foreach my $volume (@{$object->volumes}) {
|
||||
my $new_volume = $self->{model_object}->add_volume($volume);
|
||||
$new_volume->set_modifier($is_modifier);
|
||||
$new_volume->set_name(basename($input_file));
|
||||
|
||||
# apply the same translation we applied to the object
|
||||
if (($delta_x != 0.0) || ($delta_y != 0.0) || ($delta_z != 0.0)) {
|
||||
$new_volume->mesh->translate($delta_x, $delta_y, $delta_z);
|
||||
$new_volume->convex_hull->translate($delta_x, $delta_y, $delta_z);
|
||||
}
|
||||
|
||||
# set a default extruder value, since user can't add it manually
|
||||
$new_volume->config->set_ifndef('extruder', 0);
|
||||
|
||||
$self->{parts_changed} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$self->{model_object}->center_around_origin if $self->{parts_changed};
|
||||
$self->_parts_changed;
|
||||
}
|
||||
|
||||
sub on_btn_lambda {
|
||||
my ($self, $is_modifier) = @_;
|
||||
|
||||
my $dlg = Slic3r::GUI::Plater::LambdaObjectDialog->new($self);
|
||||
if ($dlg->ShowModal() == wxID_CANCEL) {
|
||||
return;
|
||||
}
|
||||
my $params = $dlg->ObjectParameter;
|
||||
my $type = "".$params->{"type"};
|
||||
my $name = "lambda-".$params->{"type"};
|
||||
my $mesh;
|
||||
|
||||
if ($type eq "box") {
|
||||
$mesh = Slic3r::TriangleMesh::cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]);
|
||||
} elsif ($type eq "cylinder") {
|
||||
$mesh = Slic3r::TriangleMesh::cylinder($params->{"cyl_r"}, $params->{"cyl_h"});
|
||||
} elsif ($type eq "sphere") {
|
||||
$mesh = Slic3r::TriangleMesh::sphere($params->{"sph_rho"});
|
||||
} elsif ($type eq "slab") {
|
||||
$mesh = Slic3r::TriangleMesh::cube($self->{model_object}->bounding_box->size->x*1.5, $self->{model_object}->bounding_box->size->y*1.5, $params->{"slab_h"});
|
||||
# box sets the base coordinate at 0,0, move to center of plate and move it up to initial_z
|
||||
$mesh->translate(-$self->{model_object}->bounding_box->size->x*1.5/2.0, -$self->{model_object}->bounding_box->size->y*1.5/2.0, $params->{"slab_z"});
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
$mesh->repair;
|
||||
my $new_volume = $self->{model_object}->add_volume(mesh => $mesh);
|
||||
$new_volume->set_modifier($is_modifier);
|
||||
$new_volume->set_name($name);
|
||||
|
||||
# set a default extruder value, since user can't add it manually
|
||||
$new_volume->config->set_ifndef('extruder', 0);
|
||||
|
||||
$self->{parts_changed} = 1;
|
||||
$self->_parts_changed;
|
||||
}
|
||||
|
||||
sub on_tree_key_down {
|
||||
my ($self, $event) = @_;
|
||||
my $keycode = $event->GetKeyCode;
|
||||
# Wx >= 0.9911
|
||||
if (defined(&Wx::TreeEvent::GetKeyEvent)) {
|
||||
if ($event->GetKeyEvent->GetModifiers & wxMOD_CONTROL) {
|
||||
if ($keycode == WXK_UP) {
|
||||
$event->Skip;
|
||||
$self->on_btn_move_up;
|
||||
} elsif ($keycode == WXK_DOWN) {
|
||||
$event->Skip;
|
||||
$self->on_btn_move_down;
|
||||
}
|
||||
} elsif ($keycode == WXK_DELETE) {
|
||||
$self->on_btn_delete;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub on_btn_move_up {
|
||||
my ($self) = @_;
|
||||
my $itemData = $self->get_selection;
|
||||
if ($itemData && $itemData->{type} eq 'volume') {
|
||||
my $volume_id = $itemData->{volume_id};
|
||||
if ($self->{model_object}->move_volume_up($volume_id)) {
|
||||
Slic3r::GUI::_3DScene::move_volume_up($self->{canvas}, $volume_id);
|
||||
$self->{parts_changed} = 1;
|
||||
$self->reload_tree($volume_id - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub on_btn_move_down {
|
||||
my ($self) = @_;
|
||||
my $itemData = $self->get_selection;
|
||||
if ($itemData && $itemData->{type} eq 'volume') {
|
||||
my $volume_id = $itemData->{volume_id};
|
||||
if ($self->{model_object}->move_volume_down($volume_id)) {
|
||||
Slic3r::GUI::_3DScene::move_volume_down($self->{canvas}, $volume_id);
|
||||
$self->{parts_changed} = 1;
|
||||
$self->reload_tree($volume_id + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub on_btn_delete {
|
||||
my ($self) = @_;
|
||||
|
||||
my $itemData = $self->get_selection;
|
||||
if ($itemData && $itemData->{type} eq 'volume') {
|
||||
my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}];
|
||||
|
||||
# if user is deleting the last solid part, throw error
|
||||
if (!$volume->modifier && scalar(grep !$_->modifier, @{$self->{model_object}->volumes}) == 1) {
|
||||
Slic3r::GUI::show_error($self, "You can't delete the last solid part from this object.");
|
||||
return;
|
||||
}
|
||||
|
||||
$self->{model_object}->delete_volume($itemData->{volume_id});
|
||||
$self->{parts_changed} = 1;
|
||||
}
|
||||
|
||||
$self->{model_object}->center_around_origin if $self->{parts_changed};
|
||||
$self->_parts_changed;
|
||||
}
|
||||
|
||||
sub on_btn_split {
|
||||
my ($self) = @_;
|
||||
|
||||
my $itemData = $self->get_selection;
|
||||
if ($itemData && $itemData->{type} eq 'volume') {
|
||||
my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}];
|
||||
my $nozzle_dmrs = $self->GetParent->GetParent->GetParent->{config}->get('nozzle_diameter');
|
||||
$self->{parts_changed} = 1 if $volume->split(scalar(@$nozzle_dmrs)) > 1;
|
||||
}
|
||||
|
||||
$self->_parts_changed;
|
||||
}
|
||||
|
||||
sub _parts_changed {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->reload_tree;
|
||||
if ($self->{canvas}) {
|
||||
Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]);
|
||||
Slic3r::GUI::_3DScene::zoom_to_volumes($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::render($self->{canvas});
|
||||
}
|
||||
}
|
||||
|
||||
sub CanClose {
|
||||
my $self = shift;
|
||||
|
||||
return 1; # skip validation for now
|
||||
|
||||
# validate options before allowing user to dismiss the dialog
|
||||
# the validate method only works on full configs so we have
|
||||
# to merge our settings with the default ones
|
||||
my $config = $self->GetParent->GetParent->GetParent->GetParent->GetParent->config->clone;
|
||||
eval {
|
||||
$config->apply($self->model_object->config);
|
||||
$config->validate;
|
||||
};
|
||||
return ! Slic3r::GUI::catch_error($self);
|
||||
}
|
||||
|
||||
sub Destroy {
|
||||
my ($self) = @_;
|
||||
$self->{canvas}->Destroy if ($self->{canvas});
|
||||
}
|
||||
|
||||
sub PartsChanged {
|
||||
my ($self) = @_;
|
||||
return $self->{parts_changed};
|
||||
}
|
||||
|
||||
sub PartSettingsChanged {
|
||||
my ($self) = @_;
|
||||
return $self->{part_settings_changed};
|
||||
}
|
||||
|
||||
sub _update_canvas {
|
||||
my ($self) = @_;
|
||||
|
||||
if ($self->{canvas}) {
|
||||
Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $self->{model_object}, 0, [0]);
|
||||
|
||||
# restore selection, if any
|
||||
if (my $itemData = $self->get_selection) {
|
||||
if ($itemData->{type} eq 'volume') {
|
||||
Slic3r::GUI::_3DScene::select_volume($self->{canvas}, $itemData->{volume_id});
|
||||
}
|
||||
}
|
||||
|
||||
Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::render($self->{canvas});
|
||||
}
|
||||
}
|
||||
|
||||
sub _update {
|
||||
my ($self) = @_;
|
||||
my ($m_x, $m_y, $m_z) = ($self->{move_options}{x}, $self->{move_options}{y}, $self->{move_options}{z});
|
||||
my ($l_x, $l_y, $l_z) = ($self->{last_coords}{x}, $self->{last_coords}{y}, $self->{last_coords}{z});
|
||||
|
||||
my $itemData = $self->get_selection;
|
||||
if ($itemData && $itemData->{type} eq 'volume') {
|
||||
my $d = Slic3r::Pointf3->new($m_x - $l_x, $m_y - $l_y, $m_z - $l_z);
|
||||
my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}];
|
||||
$volume->mesh->translate(@{$d});
|
||||
$self->{last_coords}{x} = $m_x;
|
||||
$self->{last_coords}{y} = $m_y;
|
||||
$self->{last_coords}{z} = $m_z;
|
||||
}
|
||||
|
||||
$self->{parts_changed} = 1;
|
||||
my @objects = ();
|
||||
push @objects, $self->{model_object};
|
||||
Slic3r::GUI::_3DScene::reset_volumes($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::load_model_object($self->{canvas}, $_, 0, [0]) for @objects;
|
||||
Slic3r::GUI::_3DScene::update_volumes_colors_by_extruder($self->{canvas});
|
||||
Slic3r::GUI::_3DScene::render($self->{canvas});
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,224 +0,0 @@
|
|||
# This dialog opens up when double clicked on an object line in the list at the right side of the platter.
|
||||
# One may load additional STLs and additional modifier STLs,
|
||||
# one may change the properties of the print per each modifier mesh or a Z-span.
|
||||
|
||||
package Slic3r::GUI::Plater::ObjectSettingsDialog;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx qw(:dialog :id :misc :sizer :systemsettings :notebook wxTAB_TRAVERSAL wxTheApp);
|
||||
use Wx::Event qw(EVT_BUTTON);
|
||||
use base 'Wx::Dialog';
|
||||
|
||||
# Called with
|
||||
# %params{object} of a Perl type Slic3r::GUI::Plater::Object
|
||||
# %params{model_object} of a C++ type Slic3r::ModelObject
|
||||
sub new {
|
||||
my ($class, $parent, %params) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, "Settings for " . $params{object}->name, wxDefaultPosition, [700,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
||||
$self->{$_} = $params{$_} for keys %params;
|
||||
|
||||
$self->{tabpanel} = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
|
||||
$self->{tabpanel}->AddPage($self->{parts} = Slic3r::GUI::Plater::ObjectPartsPanel->new($self->{tabpanel}, model_object => $params{model_object}), "Parts");
|
||||
$self->{tabpanel}->AddPage($self->{layers} = Slic3r::GUI::Plater::ObjectDialog::LayersTab->new($self->{tabpanel}), "Layers");
|
||||
|
||||
my $buttons = $self->CreateStdDialogButtonSizer(wxOK);
|
||||
EVT_BUTTON($self, wxID_OK, sub {
|
||||
# validate user input
|
||||
return if !$self->{parts}->CanClose;
|
||||
return if !$self->{layers}->CanClose;
|
||||
|
||||
# notify tabs
|
||||
$self->{layers}->Closing;
|
||||
|
||||
# save window size
|
||||
Slic3r::GUI::save_window_size($self, "object_settings");
|
||||
|
||||
$self->EndModal(wxID_OK);
|
||||
$self->{parts}->Destroy;
|
||||
$self->Destroy;
|
||||
});
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$sizer->Add($self->{tabpanel}, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
|
||||
$sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
|
||||
|
||||
$self->SetSizer($sizer);
|
||||
$self->SetMinSize($self->GetSize);
|
||||
|
||||
$self->Layout;
|
||||
|
||||
Slic3r::GUI::restore_window_size($self, "object_settings");
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub PartsChanged {
|
||||
my ($self) = @_;
|
||||
return $self->{parts}->PartsChanged;
|
||||
}
|
||||
|
||||
sub PartSettingsChanged {
|
||||
my ($self) = @_;
|
||||
return $self->{parts}->PartSettingsChanged || $self->{layers}->LayersChanged;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::Plater::ObjectDialog::BaseTab;
|
||||
use base 'Wx::Panel';
|
||||
|
||||
sub model_object {
|
||||
my ($self) = @_;
|
||||
# $self->GetParent->GetParent is of type Slic3r::GUI::Plater::ObjectSettingsDialog
|
||||
return $self->GetParent->GetParent->{model_object};
|
||||
}
|
||||
|
||||
package Slic3r::GUI::Plater::ObjectDialog::LayersTab;
|
||||
use Wx qw(:dialog :id :misc :sizer :systemsettings);
|
||||
use Wx::Grid;
|
||||
use Wx::Event qw(EVT_GRID_CELL_CHANGED);
|
||||
use base 'Slic3r::GUI::Plater::ObjectDialog::BaseTab';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, %params) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize);
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
|
||||
{
|
||||
my $label = Wx::StaticText->new($self, -1, "You can use this section to override the default layer height for parts of this object.",
|
||||
wxDefaultPosition, [-1, 40]);
|
||||
$label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
||||
$sizer->Add($label, 0, wxEXPAND | wxALL, 10);
|
||||
}
|
||||
|
||||
my $grid = $self->{grid} = Wx::Grid->new($self, -1, wxDefaultPosition, wxDefaultSize);
|
||||
$sizer->Add($grid, 1, wxEXPAND | wxALL, 10);
|
||||
$grid->CreateGrid(0, 3);
|
||||
$grid->DisableDragRowSize;
|
||||
$grid->HideRowLabels;
|
||||
$grid->SetColLabelValue(0, "Min Z (mm)");
|
||||
$grid->SetColLabelValue(1, "Max Z (mm)");
|
||||
$grid->SetColLabelValue(2, "Layer height (mm)");
|
||||
$grid->SetColSize($_, 135) for 0..2;
|
||||
$grid->SetDefaultCellAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
|
||||
|
||||
# load data
|
||||
foreach my $range (@{ $self->model_object->layer_height_ranges }) {
|
||||
$grid->AppendRows(1);
|
||||
my $i = $grid->GetNumberRows-1;
|
||||
$grid->SetCellValue($i, $_, $range->[$_]) for 0..2;
|
||||
}
|
||||
$grid->AppendRows(1); # append one empty row
|
||||
|
||||
EVT_GRID_CELL_CHANGED($grid, sub {
|
||||
my ($grid, $event) = @_;
|
||||
|
||||
# remove any non-numeric character
|
||||
my $value = $grid->GetCellValue($event->GetRow, $event->GetCol);
|
||||
$value =~ s/,/./g;
|
||||
$value =~ s/[^0-9.]//g;
|
||||
$grid->SetCellValue($event->GetRow, $event->GetCol, ($event->GetCol == 2) ? $self->_clamp_layer_height($value) : $value);
|
||||
|
||||
# if there's no empty row, let's append one
|
||||
for my $i (0 .. $grid->GetNumberRows) {
|
||||
if ($i == $grid->GetNumberRows) {
|
||||
# if we're here then we found no empty row
|
||||
$grid->AppendRows(1);
|
||||
last;
|
||||
}
|
||||
if (!grep $grid->GetCellValue($i, $_), 0..2) {
|
||||
# exit loop if this row is empty
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
$self->{layers_changed} = 1;
|
||||
});
|
||||
|
||||
$self->SetSizer($sizer);
|
||||
$sizer->SetSizeHints($self);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub _clamp_layer_height
|
||||
{
|
||||
my ($self, $value) = @_;
|
||||
# $self->GetParent->GetParent is of type Slic3r::GUI::Plater::ObjectSettingsDialog
|
||||
my $config = $self->GetParent->GetParent->{config};
|
||||
if ($value =~ /^[0-9,.E]+$/) {
|
||||
# Looks like a number. Validate the layer height.
|
||||
my $nozzle_dmrs = $config->get('nozzle_diameter');
|
||||
my $min_layer_heights = $config->get('min_layer_height');
|
||||
my $max_layer_heights = $config->get('max_layer_height');
|
||||
my $min_layer_height = 1000.;
|
||||
my $max_layer_height = 0.;
|
||||
my $max_nozzle_dmr = 0.;
|
||||
for (my $i = 0; $i < int(@{$nozzle_dmrs}); $i += 1) {
|
||||
$min_layer_height = $min_layer_heights->[$i] if ($min_layer_heights->[$i] < $min_layer_height);
|
||||
$max_layer_height = $max_layer_heights->[$i] if ($max_layer_heights->[$i] > $max_layer_height);
|
||||
$max_nozzle_dmr = $nozzle_dmrs ->[$i] if ($nozzle_dmrs ->[$i] > $max_nozzle_dmr );
|
||||
}
|
||||
$min_layer_height = 0.005 if ($min_layer_height < 0.005);
|
||||
$max_layer_height = $max_nozzle_dmr * 0.75 if ($max_layer_height == 0.);
|
||||
$max_layer_height = $max_nozzle_dmr if ($max_layer_height > $max_nozzle_dmr);
|
||||
return ($value < $min_layer_height) ? $min_layer_height :
|
||||
($value > $max_layer_height) ? $max_layer_height : $value;
|
||||
} else {
|
||||
# If an invalid numeric value has been entered, use the default layer height.
|
||||
return $config->get('layer_height');
|
||||
}
|
||||
}
|
||||
|
||||
sub CanClose {
|
||||
my $self = shift;
|
||||
|
||||
# validate ranges before allowing user to dismiss the dialog
|
||||
|
||||
foreach my $range ($self->_get_ranges) {
|
||||
my ($min, $max, $height) = @$range;
|
||||
if ($max <= $min) {
|
||||
Slic3r::GUI::show_error($self, "Invalid Z range $min-$max.");
|
||||
return 0;
|
||||
}
|
||||
if ($min < 0 || $max < 0) {
|
||||
Slic3r::GUI::show_error($self, "Invalid Z range $min-$max.");
|
||||
return 0;
|
||||
}
|
||||
if ($height < 0) {
|
||||
Slic3r::GUI::show_error($self, "Invalid layer height $height.");
|
||||
return 0;
|
||||
}
|
||||
# TODO: check for overlapping ranges
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub Closing {
|
||||
my $self = shift;
|
||||
|
||||
# save ranges into the plater object
|
||||
$self->model_object->set_layer_height_ranges([ $self->_get_ranges ]);
|
||||
}
|
||||
|
||||
sub _get_ranges {
|
||||
my $self = shift;
|
||||
|
||||
my @ranges = ();
|
||||
for my $i (0 .. $self->{grid}->GetNumberRows-1) {
|
||||
my ($min, $max, $height) = map $self->{grid}->GetCellValue($i, $_), 0..2;
|
||||
next if $min eq '' || $max eq '' || $height eq '';
|
||||
push @ranges, [ $min, $max, $height ];
|
||||
}
|
||||
return sort { $a->[0] <=> $b->[0] } @ranges;
|
||||
}
|
||||
|
||||
sub LayersChanged {
|
||||
my ($self) = @_;
|
||||
return $self->{layers_changed};
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,214 +0,0 @@
|
|||
# Included in ObjectSettingsDialog -> ObjectPartsPanel.
|
||||
# Maintains, displays, adds and removes overrides of slicing parameters for an object and its modifier mesh.
|
||||
|
||||
package Slic3r::GUI::Plater::OverrideSettingsPanel;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use List::Util qw(first);
|
||||
use Wx qw(:misc :sizer :button :combobox wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxTheApp);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_COMBOBOX EVT_LEFT_DOWN EVT_MENU);
|
||||
use base 'Wx::ScrolledWindow';
|
||||
|
||||
use constant ICON_MATERIAL => 0;
|
||||
use constant ICON_SOLIDMESH => 1;
|
||||
use constant ICON_MODIFIERMESH => 2;
|
||||
|
||||
use constant TYPE_OBJECT => -1;
|
||||
use constant TYPE_PART => 0;
|
||||
use constant TYPE_MODIFIER => 1;
|
||||
use constant TYPE_SUPPORT_ENFORCER => 2;
|
||||
use constant TYPE_SUPPORT_BLOCKER => 3;
|
||||
|
||||
my %icons = (
|
||||
'Advanced' => 'wand.png',
|
||||
'Extruders' => 'funnel.png',
|
||||
'Extrusion Width' => 'funnel.png',
|
||||
'Infill' => 'infill.png',
|
||||
'Layers and Perimeters' => 'layers.png',
|
||||
'Skirt and brim' => 'box.png',
|
||||
'Speed' => 'time.png',
|
||||
'Speed > Acceleration' => 'time.png',
|
||||
'Support material' => 'building.png',
|
||||
);
|
||||
|
||||
sub new {
|
||||
my ($class, $parent, %params) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
# C++ class Slic3r::DynamicPrintConfig, initially empty.
|
||||
$self->{default_config} = Slic3r::Config->new;
|
||||
$self->{config} = Slic3r::Config->new;
|
||||
# On change callback.
|
||||
$self->{on_change} = $params{on_change};
|
||||
$self->{type} = TYPE_OBJECT;
|
||||
$self->{fixed_options} = {};
|
||||
|
||||
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||
|
||||
$self->{options_sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$self->{sizer}->Add($self->{options_sizer}, 0, wxEXPAND | wxALL, 0);
|
||||
|
||||
# option selector
|
||||
{
|
||||
# create the button
|
||||
my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("add.png"), wxBITMAP_TYPE_PNG),
|
||||
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
|
||||
EVT_LEFT_DOWN($btn, sub {
|
||||
my $menu = Wx::Menu->new;
|
||||
# create category submenus
|
||||
my %categories = (); # category => submenu
|
||||
foreach my $opt_key (@{$self->{options}}) {
|
||||
if (my $cat = $Slic3r::Config::Options->{$opt_key}{category}) {
|
||||
$categories{$cat} //= Wx::Menu->new;
|
||||
}
|
||||
}
|
||||
# append submenus to main menu
|
||||
my @categories = ('Layers and Perimeters', 'Infill', 'Support material', 'Speed', 'Extruders', 'Extrusion Width', 'Advanced');
|
||||
#foreach my $cat (sort keys %categories) {
|
||||
foreach my $cat (@categories) {
|
||||
wxTheApp->append_submenu($menu, $cat, "", $categories{$cat}, undef, $icons{$cat});
|
||||
}
|
||||
# append options to submenus
|
||||
foreach my $opt_key (@{$self->{options}}) {
|
||||
my $cat = $Slic3r::Config::Options->{$opt_key}{category} or next;
|
||||
my $cb = sub {
|
||||
$self->{config}->set($opt_key, $self->{default_config}->get($opt_key));
|
||||
$self->update_optgroup;
|
||||
$self->{on_change}->($opt_key) if $self->{on_change};
|
||||
};
|
||||
wxTheApp->append_menu_item($categories{$cat}, $self->{option_labels}{$opt_key},
|
||||
$Slic3r::Config::Options->{$opt_key}{tooltip}, $cb);
|
||||
}
|
||||
$self->PopupMenu($menu, $btn->GetPosition);
|
||||
$menu->Destroy;
|
||||
});
|
||||
|
||||
my $h_sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$h_sizer->Add($btn, 0, wxALL, 0);
|
||||
$self->{sizer}->Add($h_sizer, 0, wxEXPAND | wxBOTTOM, 10);
|
||||
}
|
||||
|
||||
$self->SetSizer($self->{sizer});
|
||||
$self->SetScrollbars(0, 1, 0, 1);
|
||||
|
||||
$self->set_opt_keys($params{opt_keys}) if $params{opt_keys};
|
||||
$self->update_optgroup;
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub set_default_config {
|
||||
my ($self, $config) = @_;
|
||||
$self->{default_config} = $config;
|
||||
}
|
||||
|
||||
sub set_config {
|
||||
my ($self, $config) = @_;
|
||||
$self->{config} = $config;
|
||||
$self->update_optgroup;
|
||||
}
|
||||
|
||||
sub set_opt_keys {
|
||||
my ($self, $opt_keys) = @_;
|
||||
# sort options by category+label
|
||||
$self->{option_labels} = { map { $_ => $Slic3r::Config::Options->{$_}{full_label} // $Slic3r::Config::Options->{$_}{label} } @$opt_keys };
|
||||
$self->{options} = [ sort { $self->{option_labels}{$a} cmp $self->{option_labels}{$b} } @$opt_keys ];
|
||||
}
|
||||
|
||||
sub set_type {
|
||||
my ($self, $type) = @_;
|
||||
$self->{type} = $type;
|
||||
if ($type == TYPE_SUPPORT_ENFORCER || $type == TYPE_SUPPORT_BLOCKER) {
|
||||
$self->{btn_add}->Hide;
|
||||
} else {
|
||||
$self->{btn_add}->Show;
|
||||
}
|
||||
}
|
||||
|
||||
sub set_fixed_options {
|
||||
my ($self, $opt_keys) = @_;
|
||||
$self->{fixed_options} = { map {$_ => 1} @$opt_keys };
|
||||
$self->update_optgroup;
|
||||
}
|
||||
|
||||
sub update_optgroup {
|
||||
my $self = shift;
|
||||
|
||||
$self->{options_sizer}->Clear(1);
|
||||
return if !defined $self->{config};
|
||||
|
||||
if ($self->{type} != TYPE_OBJECT) {
|
||||
my $label = Wx::StaticText->new($self, -1, "Type:"),
|
||||
my $selection = [ "Part", "Modifier", "Support Enforcer", "Support Blocker" ];
|
||||
my $field = Wx::ComboBox->new($self, -1, $selection->[$self->{type}], wxDefaultPosition, Wx::Size->new(160, -1), $selection, wxCB_READONLY);
|
||||
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$sizer->Add($label, 1, wxEXPAND | wxALL, 5);
|
||||
$sizer->Add($field, 0, wxALL, 5);
|
||||
EVT_COMBOBOX($self, $field, sub {
|
||||
my $idx = $field->GetSelection; # get index of selected value
|
||||
$self->{on_change}->("part_type", $idx) if $self->{on_change};
|
||||
});
|
||||
$self->{options_sizer}->Add($sizer, 0, wxEXPAND | wxBOTTOM, 0);
|
||||
}
|
||||
|
||||
my %categories = ();
|
||||
if ($self->{type} != TYPE_SUPPORT_ENFORCER && $self->{type} != TYPE_SUPPORT_BLOCKER) {
|
||||
foreach my $opt_key (@{$self->{config}->get_keys}) {
|
||||
my $category = $Slic3r::Config::Options->{$opt_key}{category};
|
||||
$categories{$category} ||= [];
|
||||
push @{$categories{$category}}, $opt_key;
|
||||
}
|
||||
}
|
||||
foreach my $category (sort keys %categories) {
|
||||
my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new(
|
||||
parent => $self,
|
||||
title => $category,
|
||||
config => $self->{config},
|
||||
full_labels => 1,
|
||||
label_font => $Slic3r::GUI::small_font,
|
||||
sidetext_font => $Slic3r::GUI::small_font,
|
||||
label_width => 150,
|
||||
on_change => sub { $self->{on_change}->() if $self->{on_change} },
|
||||
extra_column => sub {
|
||||
my ($line) = @_;
|
||||
|
||||
my $opt_key = $line->get_options->[0]->opt_id; # we assume that we have one option per line
|
||||
|
||||
# disallow deleting fixed options
|
||||
return undef if $self->{fixed_options}{$opt_key};
|
||||
|
||||
my $btn = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new(Slic3r::var("delete.png"), wxBITMAP_TYPE_PNG),
|
||||
wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
|
||||
EVT_BUTTON($self, $btn, sub {
|
||||
$self->{config}->erase($opt_key);
|
||||
$self->{on_change}->() if $self->{on_change};
|
||||
wxTheApp->CallAfter(sub { $self->update_optgroup });
|
||||
});
|
||||
return $btn;
|
||||
},
|
||||
);
|
||||
foreach my $opt_key (sort @{$categories{$category}}) {
|
||||
$optgroup->append_single_option_line($opt_key);
|
||||
}
|
||||
$self->{options_sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 0);
|
||||
}
|
||||
$self->GetParent->Layout; # we need this for showing scrollbars
|
||||
}
|
||||
|
||||
# work around a wxMAC bug causing controls not being disabled when calling Disable() on a Window
|
||||
sub enable {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->{btn_add}->Enable;
|
||||
$self->Enable;
|
||||
}
|
||||
|
||||
sub disable {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->{btn_add}->Disable;
|
||||
$self->Disable;
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,144 +0,0 @@
|
|||
# Status bar at the bottom of the main screen.
|
||||
|
||||
package Slic3r::GUI::ProgressStatusBar;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Wx qw(:gauge :misc);
|
||||
use base 'Wx::StatusBar';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
|
||||
$self->{busy} = 0;
|
||||
$self->{timer} = Wx::Timer->new($self);
|
||||
$self->{prog} = Wx::Gauge->new($self, wxGA_HORIZONTAL, 100, wxDefaultPosition, wxDefaultSize);
|
||||
$self->{prog}->Hide;
|
||||
$self->{cancelbutton} = Wx::Button->new($self, -1, "Cancel", wxDefaultPosition, wxDefaultSize);
|
||||
$self->{cancelbutton}->Hide;
|
||||
|
||||
$self->SetFieldsCount(3);
|
||||
$self->SetStatusWidths(-1, 150, 155);
|
||||
|
||||
Wx::Event::EVT_TIMER($self, \&OnTimer, $self->{timer});
|
||||
Wx::Event::EVT_SIZE($self, \&OnSize);
|
||||
Wx::Event::EVT_BUTTON($self, $self->{cancelbutton}, sub {
|
||||
$self->{cancel_cb}->();
|
||||
$self->{cancelbutton}->Hide;
|
||||
});
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub DESTROY {
|
||||
my $self = shift;
|
||||
$self->{timer}->Stop if $self->{timer} && $self->{timer}->IsRunning;
|
||||
}
|
||||
|
||||
sub OnSize {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
my %fields = (
|
||||
# 0 is reserved for status text
|
||||
1 => $self->{cancelbutton},
|
||||
2 => $self->{prog},
|
||||
);
|
||||
|
||||
foreach (keys %fields) {
|
||||
my $rect = $self->GetFieldRect($_);
|
||||
my $offset = &Wx::wxGTK ? 1 : 0; # add a cosmetic 1 pixel offset on wxGTK
|
||||
my $pos = [$rect->GetX + $offset, $rect->GetY + $offset];
|
||||
$fields{$_}->Move($pos);
|
||||
$fields{$_}->SetSize($rect->GetWidth - $offset, $rect->GetHeight);
|
||||
}
|
||||
|
||||
$event->Skip;
|
||||
}
|
||||
|
||||
sub OnTimer {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
if ($self->{prog}->IsShown) {
|
||||
$self->{timer}->Stop;
|
||||
}
|
||||
$self->{prog}->Pulse if $self->{_busy};
|
||||
}
|
||||
|
||||
sub SetCancelCallback {
|
||||
my $self = shift;
|
||||
my ($cb) = @_;
|
||||
$self->{cancel_cb} = $cb;
|
||||
$cb ? $self->{cancelbutton}->Show : $self->{cancelbutton}->Hide;
|
||||
}
|
||||
|
||||
sub Run {
|
||||
my $self = shift;
|
||||
my $rate = shift || 100;
|
||||
if (!$self->{timer}->IsRunning) {
|
||||
$self->{timer}->Start($rate);
|
||||
}
|
||||
}
|
||||
|
||||
sub GetProgress {
|
||||
my $self = shift;
|
||||
return $self->{prog}->GetValue;
|
||||
}
|
||||
|
||||
sub SetProgress {
|
||||
my $self = shift;
|
||||
my ($val) = @_;
|
||||
if (!$self->{prog}->IsShown) {
|
||||
$self->ShowProgress(1);
|
||||
}
|
||||
if ($val == $self->{prog}->GetRange) {
|
||||
$self->{prog}->SetValue(0);
|
||||
$self->ShowProgress(0);
|
||||
} else {
|
||||
$self->{prog}->SetValue($val);
|
||||
}
|
||||
}
|
||||
|
||||
sub SetRange {
|
||||
my $self = shift;
|
||||
my ($val) = @_;
|
||||
|
||||
if ($val != $self->{prog}->GetRange) {
|
||||
$self->{prog}->SetRange($val);
|
||||
}
|
||||
}
|
||||
|
||||
sub ShowProgress {
|
||||
my $self = shift;
|
||||
my ($show) = @_;
|
||||
|
||||
$self->{prog}->Show($show);
|
||||
$self->{prog}->Pulse;
|
||||
}
|
||||
|
||||
sub StartBusy {
|
||||
my $self = shift;
|
||||
my $rate = shift || 100;
|
||||
|
||||
$self->{_busy} = 1;
|
||||
$self->ShowProgress(1);
|
||||
if (!$self->{timer}->IsRunning) {
|
||||
$self->{timer}->Start($rate);
|
||||
}
|
||||
}
|
||||
|
||||
sub StopBusy {
|
||||
my $self = shift;
|
||||
|
||||
$self->{timer}->Stop;
|
||||
$self->ShowProgress(0);
|
||||
$self->{prog}->SetValue(0);
|
||||
$self->{_busy} = 0;
|
||||
}
|
||||
|
||||
sub IsBusy {
|
||||
my $self = shift;
|
||||
return $self->{_busy};
|
||||
}
|
||||
|
||||
1;
|
|
@ -1,70 +0,0 @@
|
|||
package Slic3r::GUI::SystemInfo;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx qw(:font :html :misc :dialog :sizer :systemsettings :frame :id wxTheClipboard);
|
||||
use Wx::Event qw(EVT_HTML_LINK_CLICKED EVT_LEFT_DOWN EVT_BUTTON);
|
||||
use Wx::Html;
|
||||
use base 'Wx::Dialog';
|
||||
|
||||
sub new {
|
||||
my ($class, %params) = @_;
|
||||
my $self = $class->SUPER::new($params{parent}, -1, 'Slic3r Prusa Edition - System Information', wxDefaultPosition, [600, 340],
|
||||
wxDEFAULT_DIALOG_STYLE | wxMAXIMIZE_BOX | wxRESIZE_BORDER);
|
||||
$self->{text_info} = $params{text_info};
|
||||
|
||||
$self->SetBackgroundColour(Wx::wxWHITE);
|
||||
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
$self->SetSizer($vsizer);
|
||||
|
||||
# text
|
||||
my $text =
|
||||
'<html>' .
|
||||
'<body bgcolor="#ffffff" link="#808080">' .
|
||||
($params{slic3r_info} // '') .
|
||||
($params{copyright_info} // '') .
|
||||
($params{system_info} // '') .
|
||||
($params{opengl_info} // '') .
|
||||
'</body>' .
|
||||
'</html>';
|
||||
my $html = $self->{html} = Wx::HtmlWindow->new($self, -1, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO);
|
||||
my $font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||
my $size = &Wx::wxMSW ? 8 : 10;
|
||||
$html->SetFonts($font->GetFaceName, $font->GetFaceName, [$size * 1.5, $size * 1.4, $size * 1.3, $size, $size, $size, $size]);
|
||||
$html->SetBorders(10);
|
||||
$html->SetPage($text);
|
||||
$vsizer->Add($html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 0);
|
||||
EVT_HTML_LINK_CLICKED($self, $html, \&link_clicked);
|
||||
|
||||
my $buttons = $self->CreateStdDialogButtonSizer(wxOK);
|
||||
my $btn_copy_to_clipboard = Wx::Button->new($self, -1, "Copy to Clipboard", wxDefaultPosition, wxDefaultSize);
|
||||
$buttons->Insert(0, $btn_copy_to_clipboard, 0, wxLEFT, 5);
|
||||
EVT_BUTTON($self, $btn_copy_to_clipboard, \©_to_clipboard);
|
||||
$self->SetEscapeId(wxID_CLOSE);
|
||||
EVT_BUTTON($self, wxID_CLOSE, sub {
|
||||
$self->EndModal(wxID_CLOSE);
|
||||
$self->Close;
|
||||
});
|
||||
# $vsizer->Add($buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3);
|
||||
$vsizer->Add($buttons, 0, wxEXPAND | wxALL, 5);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub link_clicked {
|
||||
my ($self, $event) = @_;
|
||||
|
||||
Wx::LaunchDefaultBrowser($event->GetLinkInfo->GetHref);
|
||||
$event->Skip(0);
|
||||
}
|
||||
|
||||
sub copy_to_clipboard {
|
||||
my ($self, $event) = @_;
|
||||
my $data = $self->{text_info};
|
||||
wxTheClipboard->Open;
|
||||
wxTheClipboard->SetData(Wx::TextDataObject->new($data));
|
||||
wxTheClipboard->Close;
|
||||
}
|
||||
|
||||
1;
|
|
@ -122,6 +122,7 @@ sub line_intersection {
|
|||
: undef;
|
||||
}
|
||||
|
||||
# Used by test cases.
|
||||
sub collinear {
|
||||
my ($line1, $line2, $require_overlapping) = @_;
|
||||
my $intersection = _line_intersection(map @$_, @$line1, @$line2);
|
||||
|
@ -226,6 +227,7 @@ sub bounding_box {
|
|||
return @bb[X1,Y1,X2,Y2];
|
||||
}
|
||||
|
||||
# used by ExPolygon::size
|
||||
sub size_2D {
|
||||
my @bounding_box = bounding_box(@_);
|
||||
return (
|
||||
|
@ -234,6 +236,7 @@ sub size_2D {
|
|||
);
|
||||
}
|
||||
|
||||
# Used by sub collinear, which is used by test cases.
|
||||
# bounding_box_intersect($d, @a, @b)
|
||||
# Return true if the given bounding boxes @a and @b intersect
|
||||
# in $d dimensions. Used by sub collinear.
|
||||
|
@ -252,6 +255,7 @@ sub bounding_box_intersect {
|
|||
return 1;
|
||||
}
|
||||
|
||||
# Used by test cases.
|
||||
# this assumes a CCW rotation from $p2 to $p3 around $p1
|
||||
sub angle3points {
|
||||
my ($p1, $p2, $p3) = @_;
|
||||
|
|
|
@ -122,9 +122,9 @@ sub add_instance {
|
|||
|
||||
my $new_instance = $self->_add_instance;
|
||||
|
||||
$new_instance->set_rotation($args{rotation})
|
||||
$new_instance->set_rotations($args{rotation})
|
||||
if defined $args{rotation};
|
||||
$new_instance->set_scaling_factor($args{scaling_factor})
|
||||
$new_instance->set_scaling_factors($args{scaling_factor})
|
||||
if defined $args{scaling_factor};
|
||||
$new_instance->set_offset($args{offset})
|
||||
if defined $args{offset};
|
||||
|
|
|
@ -7,11 +7,6 @@ sub new_scale {
|
|||
return $class->new(map Slic3r::Geometry::scale($_), @_);
|
||||
}
|
||||
|
||||
sub dump_perl {
|
||||
my $self = shift;
|
||||
return sprintf "[%s,%s]", @$self;
|
||||
}
|
||||
|
||||
package Slic3r::Pointf;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
|
|
@ -10,9 +10,4 @@ sub new_scale {
|
|||
return $class->new(map [ Slic3r::Geometry::scale($_->[X]), Slic3r::Geometry::scale($_->[Y]) ], @points);
|
||||
}
|
||||
|
||||
sub dump_perl {
|
||||
my $self = shift;
|
||||
return sprintf "[%s]", join ',', map "[$_->[0],$_->[1]]", @$self;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -1,276 +0,0 @@
|
|||
# The slicing work horse.
|
||||
# Extends C++ class Slic3r::Print
|
||||
package Slic3r::Print;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use File::Basename qw(basename fileparse);
|
||||
use File::Spec;
|
||||
use List::Util qw(min max first sum);
|
||||
use Slic3r::ExtrusionLoop ':roles';
|
||||
use Slic3r::ExtrusionPath ':roles';
|
||||
use Slic3r::Flow ':roles';
|
||||
use Slic3r::Geometry qw(X Y unscale);
|
||||
use Slic3r::Geometry::Clipper qw(diff_ex union_ex intersection_ex intersection offset
|
||||
union JT_ROUND JT_SQUARE);
|
||||
use Slic3r::Print::State ':steps';
|
||||
|
||||
our $status_cb;
|
||||
|
||||
sub set_status_cb {
|
||||
my ($class, $cb) = @_;
|
||||
$status_cb = $cb;
|
||||
}
|
||||
|
||||
sub status_cb {
|
||||
return $status_cb // sub {};
|
||||
}
|
||||
|
||||
sub size {
|
||||
my $self = shift;
|
||||
return $self->bounding_box->size;
|
||||
}
|
||||
|
||||
# Slicing process, running at a background thread.
|
||||
sub process {
|
||||
my ($self) = @_;
|
||||
|
||||
Slic3r::trace(3, "Staring the slicing process.");
|
||||
$_->make_perimeters for @{$self->objects};
|
||||
|
||||
$self->status_cb->(70, "Infilling layers");
|
||||
$_->infill for @{$self->objects};
|
||||
|
||||
$_->generate_support_material for @{$self->objects};
|
||||
$self->make_skirt;
|
||||
$self->make_brim; # must come after make_skirt
|
||||
$self->make_wipe_tower;
|
||||
|
||||
# time to make some statistics
|
||||
if (0) {
|
||||
eval "use Devel::Size";
|
||||
print "MEMORY USAGE:\n";
|
||||
printf " meshes = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->meshes), @{$self->objects})/1024/1024;
|
||||
printf " layer slices = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->slices), map @{$_->layers}, @{$self->objects})/1024/1024;
|
||||
printf " region slices = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->slices), map @{$_->regions}, map @{$_->layers}, @{$self->objects})/1024/1024;
|
||||
printf " perimeters = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->perimeters), map @{$_->regions}, map @{$_->layers}, @{$self->objects})/1024/1024;
|
||||
printf " fills = %.1fMb\n", List::Util::sum(map Devel::Size::total_size($_->fills), map @{$_->regions}, map @{$_->layers}, @{$self->objects})/1024/1024;
|
||||
printf " print object = %.1fMb\n", Devel::Size::total_size($self)/1024/1024;
|
||||
}
|
||||
if (0) {
|
||||
eval "use Slic3r::Test::SectionCut";
|
||||
Slic3r::Test::SectionCut->new(print => $self)->export_svg("section_cut.svg");
|
||||
}
|
||||
Slic3r::trace(3, "Slicing process finished.")
|
||||
}
|
||||
|
||||
# G-code export process, running at a background thread.
|
||||
# The export_gcode may die for various reasons (fails to process output_filename_format,
|
||||
# write error into the G-code, cannot execute post-processing scripts).
|
||||
# It is up to the caller to show an error message.
|
||||
sub export_gcode {
|
||||
my $self = shift;
|
||||
my %params = @_;
|
||||
|
||||
# prerequisites
|
||||
$self->process;
|
||||
|
||||
# output everything to a G-code file
|
||||
# The following call may die if the output_filename_format template substitution fails.
|
||||
my $output_file = $self->output_filepath($params{output_file} // '');
|
||||
$self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : ""));
|
||||
|
||||
# The following line may die for multiple reasons.
|
||||
my $gcode = Slic3r::GCode->new;
|
||||
if (defined $params{gcode_preview_data}) {
|
||||
$gcode->do_export_w_preview($self, $output_file, $params{gcode_preview_data});
|
||||
} else {
|
||||
$gcode->do_export($self, $output_file);
|
||||
}
|
||||
|
||||
# run post-processing scripts
|
||||
if (@{$self->config->post_process}) {
|
||||
$self->status_cb->(95, "Running post-processing scripts");
|
||||
$self->config->setenv;
|
||||
for my $script (@{$self->config->post_process}) {
|
||||
# Ignore empty post processing script lines.
|
||||
next if $script =~ /^\s*$/;
|
||||
Slic3r::debugf " '%s' '%s'\n", $script, $output_file;
|
||||
# -x doesn't return true on Windows except for .exe files
|
||||
if (($^O eq 'MSWin32') ? !(-e $script) : !(-x $script)) {
|
||||
die "The configured post-processing script is not executable: check permissions. ($script)\n";
|
||||
}
|
||||
if ($^O eq 'MSWin32' && $script =~ /\.[pP][lL]/) {
|
||||
# The current process (^X) may be slic3r.exe or slic3r-console.exe.
|
||||
# Replace it with the current perl interpreter.
|
||||
my($filename, $directories, $suffix) = fileparse($^X);
|
||||
$filename =~ s/^slic3r.*$/perl5\.24\.0\.exe/;
|
||||
my $interpreter = $directories . $filename;
|
||||
system($interpreter, $script, $output_file);
|
||||
} else {
|
||||
system($script, $output_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Export SVG slices for the offline SLA printing.
|
||||
# The export_svg is expected to be executed inside an eval block.
|
||||
sub export_svg {
|
||||
my $self = shift;
|
||||
my %params = @_;
|
||||
|
||||
$_->slice for @{$self->objects};
|
||||
|
||||
my $fh = $params{output_fh};
|
||||
if (!$fh) {
|
||||
# The following line may die if the output_filename_format template substitution fails.
|
||||
my $output_file = $self->output_filepath($params{output_file});
|
||||
$output_file =~ s/\.[gG][cC][oO][dD][eE]$/.svg/;
|
||||
Slic3r::open(\$fh, ">", $output_file) or die "Failed to open $output_file for writing\n";
|
||||
print "Exporting to $output_file..." unless $params{quiet};
|
||||
}
|
||||
|
||||
my $print_bb = $self->bounding_box;
|
||||
my $print_size = $print_bb->size;
|
||||
print $fh sprintf <<"EOF", unscale($print_size->[X]), unscale($print_size->[Y]);
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg width="%s" height="%s" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:slic3r="http://slic3r.org/namespaces/slic3r">
|
||||
<!--
|
||||
Generated using Slic3r $Slic3r::VERSION
|
||||
http://slic3r.org/
|
||||
-->
|
||||
EOF
|
||||
|
||||
my $print_polygon = sub {
|
||||
my ($polygon, $type) = @_;
|
||||
printf $fh qq{ <polygon slic3r:type="%s" points="%s" style="fill: %s" />\n},
|
||||
$type, (join ' ', map { join ',', map unscale $_, @$_ } @$polygon),
|
||||
($type eq 'contour' ? 'white' : 'black');
|
||||
};
|
||||
|
||||
my @layers = sort { $a->print_z <=> $b->print_z }
|
||||
map { @{$_->layers}, @{$_->support_layers} }
|
||||
@{$self->objects};
|
||||
|
||||
my $layer_id = -1;
|
||||
my @previous_layer_slices = ();
|
||||
for my $layer (@layers) {
|
||||
$layer_id++;
|
||||
if ($layer->slice_z == -1) {
|
||||
printf $fh qq{ <g id="layer%d">\n}, $layer_id;
|
||||
} else {
|
||||
printf $fh qq{ <g id="layer%d" slic3r:z="%s">\n}, $layer_id, unscale($layer->slice_z);
|
||||
}
|
||||
|
||||
my @current_layer_slices = ();
|
||||
# sort slices so that the outermost ones come first
|
||||
my @slices = sort { $a->contour->contains_point($b->contour->first_point) ? 0 : 1 } @{$layer->slices};
|
||||
foreach my $copy (@{$layer->object->_shifted_copies}) {
|
||||
foreach my $slice (@slices) {
|
||||
my $expolygon = $slice->clone;
|
||||
$expolygon->translate(@$copy);
|
||||
$expolygon->translate(-$print_bb->x_min, -$print_bb->y_min);
|
||||
$print_polygon->($expolygon->contour, 'contour');
|
||||
$print_polygon->($_, 'hole') for @{$expolygon->holes};
|
||||
push @current_layer_slices, $expolygon;
|
||||
}
|
||||
}
|
||||
# generate support material
|
||||
if ($self->has_support_material && $layer->id > 0) {
|
||||
my (@supported_slices, @unsupported_slices) = ();
|
||||
foreach my $expolygon (@current_layer_slices) {
|
||||
my $intersection = intersection_ex(
|
||||
[ map @$_, @previous_layer_slices ],
|
||||
[ @$expolygon ],
|
||||
);
|
||||
@$intersection
|
||||
? push @supported_slices, $expolygon
|
||||
: push @unsupported_slices, $expolygon;
|
||||
}
|
||||
my @supported_points = map @$_, @$_, @supported_slices;
|
||||
foreach my $expolygon (@unsupported_slices) {
|
||||
# look for the nearest point to this island among all
|
||||
# supported points
|
||||
my $contour = $expolygon->contour;
|
||||
my $support_point = $contour->first_point->nearest_point(\@supported_points)
|
||||
or next;
|
||||
my $anchor_point = $support_point->nearest_point([ @$contour ]);
|
||||
printf $fh qq{ <line x1="%s" y1="%s" x2="%s" y2="%s" style="stroke-width: 2; stroke: white" />\n},
|
||||
map @$_, $support_point, $anchor_point;
|
||||
}
|
||||
}
|
||||
print $fh qq{ </g>\n};
|
||||
@previous_layer_slices = @current_layer_slices;
|
||||
}
|
||||
|
||||
print $fh "</svg>\n";
|
||||
close $fh;
|
||||
print "Done.\n" unless $params{quiet};
|
||||
}
|
||||
|
||||
sub make_skirt {
|
||||
my $self = shift;
|
||||
|
||||
# prerequisites
|
||||
$_->make_perimeters for @{$self->objects};
|
||||
$_->infill for @{$self->objects};
|
||||
$_->generate_support_material for @{$self->objects};
|
||||
|
||||
return if $self->step_done(STEP_SKIRT);
|
||||
|
||||
$self->set_step_started(STEP_SKIRT);
|
||||
$self->skirt->clear;
|
||||
if ($self->has_skirt) {
|
||||
$self->status_cb->(88, "Generating skirt");
|
||||
$self->_make_skirt();
|
||||
}
|
||||
$self->set_step_done(STEP_SKIRT);
|
||||
}
|
||||
|
||||
sub make_brim {
|
||||
my $self = shift;
|
||||
|
||||
# prerequisites
|
||||
$_->make_perimeters for @{$self->objects};
|
||||
$_->infill for @{$self->objects};
|
||||
$_->generate_support_material for @{$self->objects};
|
||||
$self->make_skirt;
|
||||
|
||||
return if $self->step_done(STEP_BRIM);
|
||||
|
||||
$self->set_step_started(STEP_BRIM);
|
||||
# since this method must be idempotent, we clear brim paths *before*
|
||||
# checking whether we need to generate them
|
||||
$self->brim->clear;
|
||||
if ($self->config->brim_width > 0) {
|
||||
$self->status_cb->(88, "Generating brim");
|
||||
$self->_make_brim;
|
||||
}
|
||||
|
||||
$self->set_step_done(STEP_BRIM);
|
||||
}
|
||||
|
||||
sub make_wipe_tower {
|
||||
my $self = shift;
|
||||
|
||||
# prerequisites
|
||||
$_->make_perimeters for @{$self->objects};
|
||||
$_->infill for @{$self->objects};
|
||||
$_->generate_support_material for @{$self->objects};
|
||||
$self->make_skirt;
|
||||
$self->make_brim;
|
||||
|
||||
return if $self->step_done(STEP_WIPE_TOWER);
|
||||
|
||||
$self->set_step_started(STEP_WIPE_TOWER);
|
||||
$self->_clear_wipe_tower;
|
||||
if ($self->has_wipe_tower) {
|
||||
# $self->status_cb->(95, "Generating wipe tower");
|
||||
$self->_make_wipe_tower;
|
||||
}
|
||||
$self->set_step_done(STEP_WIPE_TOWER);
|
||||
}
|
||||
|
||||
1;
|
|
@ -21,95 +21,4 @@ sub support_layers {
|
|||
return [ map $self->get_support_layer($_), 0..($self->support_layer_count - 1) ];
|
||||
}
|
||||
|
||||
# 1) Decides Z positions of the layers,
|
||||
# 2) Initializes layers and their regions
|
||||
# 3) Slices the object meshes
|
||||
# 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
|
||||
# 5) Applies size compensation (offsets the slices in XY plane)
|
||||
# 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
|
||||
# Resulting expolygons of layer regions are marked as Internal.
|
||||
#
|
||||
# this should be idempotent
|
||||
sub slice {
|
||||
my $self = shift;
|
||||
|
||||
return if $self->step_done(STEP_SLICE);
|
||||
$self->set_step_started(STEP_SLICE);
|
||||
$self->print->status_cb->(10, "Processing triangulated mesh");
|
||||
|
||||
$self->_slice;
|
||||
|
||||
my $warning = $self->_fix_slicing_errors;
|
||||
warn $warning if (defined($warning) && $warning ne '');
|
||||
|
||||
# simplify slices if required
|
||||
$self->_simplify_slices(scale($self->print->config->resolution))
|
||||
if ($self->print->config->resolution);
|
||||
|
||||
die "No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"
|
||||
if !@{$self->layers};
|
||||
|
||||
$self->set_step_done(STEP_SLICE);
|
||||
}
|
||||
|
||||
# 1) Merges typed region slices into stInternal type.
|
||||
# 2) Increases an "extra perimeters" counter at region slices where needed.
|
||||
# 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
|
||||
sub make_perimeters {
|
||||
my ($self) = @_;
|
||||
|
||||
# prerequisites
|
||||
$self->slice;
|
||||
|
||||
if (! $self->step_done(STEP_PERIMETERS)) {
|
||||
$self->print->status_cb->(20, "Generating perimeters");
|
||||
$self->_make_perimeters;
|
||||
}
|
||||
}
|
||||
|
||||
sub prepare_infill {
|
||||
my ($self) = @_;
|
||||
|
||||
# prerequisites
|
||||
$self->make_perimeters;
|
||||
|
||||
return if $self->step_done(STEP_PREPARE_INFILL);
|
||||
$self->set_step_started(STEP_PREPARE_INFILL);
|
||||
$self->print->status_cb->(30, "Preparing infill");
|
||||
|
||||
$self->_prepare_infill;
|
||||
|
||||
$self->set_step_done(STEP_PREPARE_INFILL);
|
||||
}
|
||||
|
||||
sub infill {
|
||||
my ($self) = @_;
|
||||
|
||||
# prerequisites
|
||||
$self->prepare_infill;
|
||||
$self->_infill;
|
||||
}
|
||||
|
||||
sub generate_support_material {
|
||||
my $self = shift;
|
||||
|
||||
# prerequisites
|
||||
$self->slice;
|
||||
|
||||
return if $self->step_done(STEP_SUPPORTMATERIAL);
|
||||
$self->set_step_started(STEP_SUPPORTMATERIAL);
|
||||
|
||||
$self->clear_support_layers;
|
||||
|
||||
if (($self->config->support_material || $self->config->raft_layers > 0) && scalar(@{$self->layers}) > 1) {
|
||||
$self->print->status_cb->(85, "Generating support material");
|
||||
# New supports, C++ implementation.
|
||||
$self->_generate_support_material;
|
||||
}
|
||||
|
||||
$self->set_step_done(STEP_SUPPORTMATERIAL);
|
||||
my $stats = sprintf "Weight: %.1fg, Cost: %.1f" , $self->print->total_weight, $self->print->total_cost;
|
||||
$self->print->status_cb->(85, $stats);
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -38,11 +38,6 @@ has 'duplicate_grid' => (
|
|||
default => sub { [1,1] },
|
||||
);
|
||||
|
||||
has 'status_cb' => (
|
||||
is => 'rw',
|
||||
default => sub { sub {} },
|
||||
);
|
||||
|
||||
has 'print_center' => (
|
||||
is => 'rw',
|
||||
default => sub { Slic3r::Pointf->new(100,100) },
|
||||
|
@ -90,33 +85,18 @@ sub set_model {
|
|||
}
|
||||
}
|
||||
|
||||
sub _before_export {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_print->set_status_cb($self->status_cb);
|
||||
$self->_print->validate;
|
||||
}
|
||||
|
||||
sub _after_export {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_print->set_status_cb(undef);
|
||||
}
|
||||
|
||||
sub export_gcode {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_before_export;
|
||||
$self->_print->export_gcode(output_file => $self->output_file);
|
||||
$self->_after_export;
|
||||
$self->_print->validate;
|
||||
$self->_print->export_gcode($self->output_file // '');
|
||||
}
|
||||
|
||||
sub export_svg {
|
||||
sub export_png {
|
||||
my ($self) = @_;
|
||||
|
||||
$self->_before_export;
|
||||
|
||||
$self->_print->export_svg(output_file => $self->output_file);
|
||||
$self->_print->export_png(output_file => $self->output_file);
|
||||
|
||||
$self->_after_export;
|
||||
}
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
package Slic3r::SVG;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use SVG;
|
||||
|
||||
use constant X => 0;
|
||||
use constant Y => 1;
|
||||
|
||||
our $filltype = 'evenodd';
|
||||
|
||||
sub factor {
|
||||
return &Slic3r::SCALING_FACTOR * 10;
|
||||
}
|
||||
|
||||
sub svg {
|
||||
my $svg = SVG->new(width => 200 * 10, height => 200 * 10);
|
||||
my $marker_end = $svg->marker(
|
||||
id => "endArrow",
|
||||
viewBox => "0 0 10 10",
|
||||
refX => "1",
|
||||
refY => "5",
|
||||
markerUnits => "strokeWidth",
|
||||
orient => "auto",
|
||||
markerWidth => "10",
|
||||
markerHeight => "8",
|
||||
);
|
||||
$marker_end->polyline(
|
||||
points => "0,0 10,5 0,10 1,5",
|
||||
fill => "darkblue",
|
||||
);
|
||||
|
||||
return $svg;
|
||||
}
|
||||
|
||||
sub output {
|
||||
my ($filename, @things) = @_;
|
||||
|
||||
my $svg = svg();
|
||||
my $arrows = 1;
|
||||
|
||||
while (my $type = shift @things) {
|
||||
my $value = shift @things;
|
||||
|
||||
if ($type eq 'no_arrows') {
|
||||
$arrows = 0;
|
||||
} elsif ($type =~ /^(?:(.+?)_)?expolygons$/) {
|
||||
my $colour = $1;
|
||||
$value = [ map $_->pp, @$value ];
|
||||
|
||||
my $g = $svg->group(
|
||||
style => {
|
||||
'stroke-width' => 0,
|
||||
'stroke' => $colour || 'black',
|
||||
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
|
||||
'fill-type' => $filltype,
|
||||
},
|
||||
);
|
||||
foreach my $expolygon (@$value) {
|
||||
my $points = join ' ', map "M $_ z", map join(" ", reverse map $_->[0]*factor() . " " . $_->[1]*factor(), @$_), @$expolygon;
|
||||
$g->path(
|
||||
d => $points,
|
||||
);
|
||||
}
|
||||
} elsif ($type =~ /^(?:(.+?)_)?(polygon|polyline)s$/) {
|
||||
my ($colour, $method) = ($1, $2);
|
||||
$value = [ map $_->pp, @$value ];
|
||||
|
||||
my $g = $svg->group(
|
||||
style => {
|
||||
'stroke-width' => ($method eq 'polyline') ? 1 : 0,
|
||||
'stroke' => $colour || 'black',
|
||||
'fill' => ($type !~ /polygons/ ? 'none' : ($colour || 'grey')),
|
||||
},
|
||||
);
|
||||
foreach my $polygon (@$value) {
|
||||
my $path = $svg->get_path(
|
||||
'x' => [ map($_->[X] * factor(), @$polygon) ],
|
||||
'y' => [ map($_->[Y] * factor(), @$polygon) ],
|
||||
-type => 'polygon',
|
||||
);
|
||||
$g->$method(
|
||||
%$path,
|
||||
'marker-end' => !$arrows ? "" : "url(#endArrow)",
|
||||
);
|
||||
}
|
||||
} elsif ($type =~ /^(?:(.+?)_)?points$/) {
|
||||
my $colour = $1 // 'black';
|
||||
my $r = $colour eq 'black' ? 1 : 3;
|
||||
$value = [ map $_->pp, @$value ];
|
||||
|
||||
my $g = $svg->group(
|
||||
style => {
|
||||
'stroke-width' => 2,
|
||||
'stroke' => $colour,
|
||||
'fill' => $colour,
|
||||
},
|
||||
);
|
||||
foreach my $point (@$value) {
|
||||
$g->circle(
|
||||
cx => $point->[X] * factor(),
|
||||
cy => $point->[Y] * factor(),
|
||||
r => $r,
|
||||
);
|
||||
}
|
||||
} elsif ($type =~ /^(?:(.+?)_)?lines$/) {
|
||||
my $colour = $1;
|
||||
$value = [ map $_->pp, @$value ];
|
||||
|
||||
my $g = $svg->group(
|
||||
style => {
|
||||
'stroke-width' => 2,
|
||||
},
|
||||
);
|
||||
foreach my $line (@$value) {
|
||||
$g->line(
|
||||
x1 => $line->[0][X] * factor(),
|
||||
y1 => $line->[0][Y] * factor(),
|
||||
x2 => $line->[1][X] * factor(),
|
||||
y2 => $line->[1][Y] * factor(),
|
||||
style => {
|
||||
'stroke' => $colour || 'black',
|
||||
},
|
||||
'marker-end' => !$arrows ? "" : "url(#endArrow)",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_svg($svg, $filename);
|
||||
}
|
||||
|
||||
sub write_svg {
|
||||
my ($svg, $filename) = @_;
|
||||
|
||||
Slic3r::open(\my $fh, '>', $filename);
|
||||
print $fh $svg->xmlify;
|
||||
close $fh;
|
||||
printf "SVG written to %s\n", $filename;
|
||||
}
|
||||
|
||||
1;
|
|
@ -158,8 +158,12 @@ sub model {
|
|||
$object->add_volume(mesh => $mesh, material_id => $model_name);
|
||||
$object->add_instance(
|
||||
offset => Slic3r::Pointf->new(0,0),
|
||||
rotation => $params{rotation} // 0,
|
||||
scaling_factor => $params{scale} // 1,
|
||||
# 3D full transform
|
||||
rotation => Slic3r::Pointf3->new(0, 0, $params{rotation} // 0),
|
||||
scaling_factor => Slic3r::Pointf3->new($params{scale} // 1, $params{scale} // 1, $params{scale} // 1),
|
||||
# old transform
|
||||
# rotation => $params{rotation} // 0,
|
||||
# scaling_factor => $params{scale} // 1,
|
||||
);
|
||||
return $model;
|
||||
}
|
||||
|
@ -208,8 +212,9 @@ sub gcode {
|
|||
my $gcode_temp_path = abs_path($0) . '.gcode.temp';
|
||||
# Remove the existing temp file.
|
||||
unlink $gcode_temp_path;
|
||||
$print->set_status_silent;
|
||||
$print->process;
|
||||
$print->export_gcode(output_file => $gcode_temp_path, quiet => 1);
|
||||
$print->export_gcode($gcode_temp_path);
|
||||
# Read the temoprary G-code file.
|
||||
my $gcode;
|
||||
{
|
||||
|
|
BIN
resources/icons/add_object.png
Normal file
After Width: | Height: | Size: 829 B |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 36 KiB |
BIN
resources/icons/colorchange_add_off.png
Normal file
After Width: | Height: | Size: 600 B |
BIN
resources/icons/colorchange_add_on.png
Normal file
After Width: | Height: | Size: 695 B |
BIN
resources/icons/colorchange_delete_off.png
Normal file
After Width: | Height: | Size: 589 B |
BIN
resources/icons/colorchange_delete_on.png
Normal file
After Width: | Height: | Size: 628 B |
BIN
resources/icons/disclosure_triangle_close.png
Normal file
After Width: | Height: | Size: 212 B |
BIN
resources/icons/disclosure_triangle_open.png
Normal file
After Width: | Height: | Size: 210 B |
BIN
resources/icons/down_half_circle.png
Normal file
After Width: | Height: | Size: 631 B |
BIN
resources/icons/erase.png
Normal file
After Width: | Height: | Size: 488 B |
BIN
resources/icons/exclamation_mark_.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
resources/icons/lambda.png
Normal file
After Width: | Height: | Size: 913 B |
BIN
resources/icons/lambda_.png
Normal file
After Width: | Height: | Size: 422 B |
BIN
resources/icons/left_half_circle.png
Normal file
After Width: | Height: | Size: 651 B |
BIN
resources/icons/mode_expert.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
resources/icons/mode_expert_.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
resources/icons/mode_middle.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
resources/icons/mode_middle_.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/icons/mode_simple.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
resources/icons/mode_simple_.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/icons/object.png
Normal file
After Width: | Height: | Size: 1,017 B |
BIN
resources/icons/one_layer_lock_off.png
Normal file
After Width: | Height: | Size: 1,001 B |
BIN
resources/icons/one_layer_lock_on.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/icons/one_layer_unlock_off.png
Normal file
After Width: | Height: | Size: 997 B |
BIN
resources/icons/one_layer_unlock_on.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/icons/overlay/cut_hover.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
resources/icons/overlay/cut_off.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
resources/icons/overlay/cut_on.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icons/overlay/move_hover.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
resources/icons/overlay/move_off.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
resources/icons/overlay/move_on.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icons/overlay/sla_support_points_hover.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
resources/icons/overlay/sla_support_points_off.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
resources/icons/overlay/sla_support_points_on.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/icons/printers/PrusaResearch_SL1.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
resources/icons/right_half_circle.png
Normal file
After Width: | Height: | Size: 654 B |
BIN
resources/icons/shape_ungroup_o.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
resources/icons/shape_ungroup_p.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
resources/icons/split.png
Normal file
After Width: | Height: | Size: 1,021 B |
BIN
resources/icons/support_blocker_.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
resources/icons/support_enforcer_.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
resources/icons/toolbar.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
resources/icons/up_half_circle.png
Normal file
After Width: | Height: | Size: 650 B |
|
@ -1,17 +1,6 @@
|
|||
min_slic3r_version = 1.41.1
|
||||
0.3.2 New MK2.5 and MK3 FW versions
|
||||
0.3.1 New MK2.5 and MK3 FW versions
|
||||
0.3.0 New MK2.5 and MK3 FW version
|
||||
min_slic3r_version = 1.42.0-alpha
|
||||
0.4.0-alpha2 First SLA profiles
|
||||
min_slic3r_version = 1.41.0-alpha
|
||||
0.2.9 New MK2.5 and MK3 FW versions
|
||||
0.2.8 New MK2.5 and MK3 FW version
|
||||
min_slic3r_version = 1.41.1
|
||||
0.2.7 New MK2.5 and MK3 FW version
|
||||
0.2.6 Added MMU2 MK2.5 settings
|
||||
min_slic3r_version = 1.41.0-alpha
|
||||
0.2.5 Prusament is out - added prusament settings
|
||||
0.2.4 Added soluble support profiles for MMU2
|
||||
0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit
|
||||
0.2.2 Edited MMU2 Single mode purge line
|
||||
0.2.1 Added PET and BVOH settings for MMU2
|
||||
0.2.0-beta5 Fixed MMU1 ramming parameters
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
name = Prusa Research
|
||||
# Configuration version of this file. Config file will only be installed, if the config_version differs.
|
||||
# This means, the server may force the Slic3r configuration to be downgraded.
|
||||
config_version = 0.3.2
|
||||
config_version = 0.4.0-alpha2
|
||||
# Where to get the updates from?
|
||||
config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/
|
||||
|
||||
|
@ -39,6 +39,10 @@ variants = 0.4; 0.6
|
|||
name = Original Prusa i3 MK2.5 MMU 2.0
|
||||
variants = 0.4
|
||||
|
||||
[printer_model:SL1]
|
||||
name = Original Prusa SL1
|
||||
variants = default; dummy
|
||||
|
||||
# All presets starting with asterisk, for example *common*, are intermediate and they will
|
||||
# not make it into the user interface.
|
||||
|
||||
|
@ -1133,7 +1137,22 @@ min_fan_speed = 100
|
|||
start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode"
|
||||
temperature = 220
|
||||
|
||||
[sla_material:*common*]
|
||||
layer_height = 0.05
|
||||
initial_layer_height = 0.3
|
||||
exposure_time = 10
|
||||
initial_exposure_time = 15
|
||||
material_correction_printing = 1, 1, 1
|
||||
material_correction_curing = 1, 1, 1
|
||||
|
||||
[sla_material:Material 1]
|
||||
inherits = *common*
|
||||
|
||||
[sla_material:Material 2]
|
||||
inherits = *common*
|
||||
|
||||
[printer:*common*]
|
||||
printer_technology = FFF
|
||||
bed_shape = 0x0,250x0,250x210,0x210
|
||||
before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\n\n
|
||||
between_objects_gcode =
|
||||
|
@ -1444,6 +1463,24 @@ extruder_colour = #FF8000;#0080FF;#00FFFF;#FF4F4F;#9FFF9F
|
|||
start_gcode = M107\nM115 U3.4.1 ; tell printer latest fw version\nM83 ; extruder relative mode\nM104 S[first_layer_temperature] ; set extruder temp\nM140 S[first_layer_bed_temperature] ; set bed temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM109 S[first_layer_temperature] ; wait for extruder temp\nG28 W ; home all without mesh bed level\nG80 ; mesh bed leveling\nG21 ; set units to millimeters\n\n; Send the filament type to the MMU2.0 unit.\n; E stands for extruder number, F stands for filament type (0: default; 1:flex; 2: PVA)\nM403 E0 F{"" + ((filament_type[0]=="FLEX") ? 1 : ((filament_type[0]=="PVA") ? 2 : 0))}\nM403 E1 F{"" + ((filament_type[1]=="FLEX") ? 1 : ((filament_type[1]=="PVA") ? 2 : 0))}\nM403 E2 F{"" + ((filament_type[2]=="FLEX") ? 1 : ((filament_type[2]=="PVA") ? 2 : 0))}\nM403 E3 F{"" + ((filament_type[3]=="FLEX") ? 1 : ((filament_type[3]=="PVA") ? 2 : 0))}\nM403 E4 F{"" + ((filament_type[4]=="FLEX") ? 1 : ((filament_type[4]=="PVA") ? 2 : 0))}\n\n{if not has_single_extruder_multi_material_priming}\n;go outside print area\nG1 Y-3.0 F1000.0\nG1 Z0.4 F1000.0\n; select extruder\nT[initial_tool]\n; initial load\nG1 X55.0 E32.0 F1073.0\nG1 X5.0 E32.0 F1800.0\nG1 X55.0 E8.0 F2000.0\nG1 Z0.3 F1000.0\nG92 E0.0\nG1 X240.0 E25.0 F2200.0\nG1 Y-2.0 F1000.0\nG1 X55.0 E25 F1400.0\nG1 Z0.20 F1000.0\nG1 X5.0 E4.0 F1000.0\nG92 E0.0\n{endif}\n\nM221 S{if layer_height<0.075}100{else}95{endif}\nG90 ; use absolute coordinates\nM83 ; use relative distances for extrusion\nG92 E0.0\n
|
||||
end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG1 E2 F5000\nG1 E2 F5500\nG1 E2 F6000\nG1 E-15.0000 F5800\nG1 E-20.0000 F5500\nG1 E10.0000 F3000\nG1 E-10.0000 F3100\nG1 E10.0000 F3150\nG1 E-10.0000 F3250\nG1 E10.0000 F3300\n{endif}\n\n; Unload filament\nM702 C\n\nG4 ; wait\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\n; Lift print head a bit\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+30, max_print_height)}{endif} ; Move print head up\nG1 X0 Y200; home X axis\nM84 ; disable motors\n
|
||||
|
||||
[printer:Original Prusa SL1]
|
||||
printer_technology = SLA
|
||||
printer_model = SL1
|
||||
printer_variant = default
|
||||
default_sla_material_profile = Material 1
|
||||
bed_shape = 0x0,150x0,150x100,0x100
|
||||
max_print_height = 100
|
||||
display_width = 150
|
||||
display_height = 100
|
||||
display_pixels_x = 2000
|
||||
display_pixels_y = 1000
|
||||
printer_correction = 1,1,1
|
||||
|
||||
[printer:Original Prusa SL1 dummy]
|
||||
inherits = Original Prusa SL1
|
||||
printer_variant = dummy
|
||||
default_sla_material_profile = Material 2
|
||||
|
||||
# The obsolete presets will be removed when upgrading from the legacy configuration structure (up to Slic3r 1.39.2) to 1.40.0 and newer.
|
||||
[obsolete_presets]
|
||||
print="0.05mm DETAIL 0.25 nozzle";"0.05mm DETAIL MK3";"0.05mm DETAIL";"0.20mm NORMAL MK3";"0.35mm FAST MK3";"print:0.15mm OPTIMAL MK3 MMU2";"print:0.20mm FAST MK3 MMU2"
|
||||
|
|
2
sandboxes/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
add_subdirectory(slabasebed)
|
||||
add_subdirectory(slasupporttree)
|
2
sandboxes/slabasebed/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
add_executable(slabasebed EXCLUDE_FROM_ALL slabasebed.cpp)
|
||||
target_link_libraries(slabasebed libslic3r)
|
43
sandboxes/slabasebed/slabasebed.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <libslic3r.h>
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "SLABasePool.hpp"
|
||||
#include "benchmark.h"
|
||||
|
||||
const std::string USAGE_STR = {
|
||||
"Usage: slabasebed 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;
|
||||
}
|
||||
|
||||
TriangleMesh model;
|
||||
Benchmark bench;
|
||||
|
||||
model.ReadSTLFile(argv[1]);
|
||||
model.align_to_origin();
|
||||
|
||||
ExPolygons ground_slice;
|
||||
TriangleMesh basepool;
|
||||
|
||||
sla::ground_layer(model, ground_slice, 0.1f);
|
||||
|
||||
bench.start();
|
||||
sla::create_base_pool(ground_slice, basepool);
|
||||
bench.stop();
|
||||
|
||||
cout << "Base pool creation time: " << std::setprecision(10)
|
||||
<< bench.getElapsedSec() << " seconds." << endl;
|
||||
|
||||
basepool.write_ascii("out.stl");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
2
sandboxes/slasupporttree/CMakeLists.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
add_executable(slasupporttree EXCLUDE_FROM_ALL slasupporttree.cpp)
|
||||
target_link_libraries(slasupporttree libslic3r)
|
48
sandboxes/slasupporttree/slasupporttree.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <libslic3r.h>
|
||||
#include "TriangleMesh.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "callback.hpp"
|
||||
#include "SLA/SLASupportTree.hpp"
|
||||
#include "benchmark.h"
|
||||
|
||||
const std::string USAGE_STR = {
|
||||
"Usage: slasupporttree stlfilename.stl"
|
||||
};
|
||||
|
||||
void confess_at(const char * /*file*/,
|
||||
int /*line*/,
|
||||
const char * /*func*/,
|
||||
const char * /*pat*/,
|
||||
...) {}
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void PerlCallback::deregister_callback() {}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Benchmark bench;
|
||||
TriangleMesh result;
|
||||
|
||||
bench.start();
|
||||
sla::create_head(result, 3, 1, 4);
|
||||
bench.stop();
|
||||
|
||||
cout << "Support tree creation time: " << std::setprecision(10)
|
||||
<< bench.getElapsedSec() << " seconds." << endl;
|
||||
|
||||
result.write_ascii("out.stl");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
21
slic3r.pl
|
@ -38,11 +38,10 @@ my %cli_options = ();
|
|||
'load=s@' => \$opt{load},
|
||||
'autosave=s' => \$opt{autosave},
|
||||
'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config},
|
||||
'no-controller' => \$opt{no_controller},
|
||||
'no-plater' => \$opt{no_plater},
|
||||
'gui-mode=s' => \$opt{obsolete_ignore_this_option_gui_mode},
|
||||
'datadir=s' => \$opt{datadir},
|
||||
'export-svg' => \$opt{export_svg},
|
||||
'export-png' => \$opt{export_png},
|
||||
'merge|m' => \$opt{merge},
|
||||
'repair' => \$opt{repair},
|
||||
'cut=f' => \$opt{cut},
|
||||
|
@ -107,10 +106,10 @@ if ((!@ARGV || $opt{gui}) && !$opt{no_gui} && !$opt{save} && eval "require Slic3
|
|||
{
|
||||
no warnings 'once';
|
||||
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // '');
|
||||
$Slic3r::GUI::no_controller = $opt{no_controller};
|
||||
$Slic3r::GUI::no_plater = $opt{no_plater};
|
||||
$Slic3r::GUI::autosave = $opt{autosave};
|
||||
}
|
||||
Slic3r::GUI::set_gui_appctl();
|
||||
$gui = Slic3r::GUI->new;
|
||||
#setlocale(LC_NUMERIC, 'C');
|
||||
$gui->{mainframe}->load_config_file($_) for @{$opt{load}};
|
||||
|
@ -123,6 +122,9 @@ if ((!@ARGV || $opt{gui}) && !$opt{no_gui} && !$opt{save} && eval "require Slic3
|
|||
die $@ if $@ && $opt{gui};
|
||||
|
||||
if (@ARGV) { # slicing from command line
|
||||
Slic3r::GUI::set_cli_appctl();
|
||||
my $appctl = Slic3r::AppController->new();
|
||||
|
||||
$config->validate;
|
||||
|
||||
if ($opt{repair}) {
|
||||
|
@ -202,10 +204,6 @@ if (@ARGV) { # slicing from command line
|
|||
duplicate_grid => $opt{duplicate_grid} // [1,1],
|
||||
print_center => $opt{print_center} // Slic3r::Pointf->new(100,100),
|
||||
dont_arrange => $opt{dont_arrange} // 0,
|
||||
status_cb => sub {
|
||||
my ($percent, $message) = @_;
|
||||
printf "=> %s\n", $message;
|
||||
},
|
||||
output_file => $opt{output},
|
||||
);
|
||||
|
||||
|
@ -215,8 +213,11 @@ if (@ARGV) { # slicing from command line
|
|||
# Do the apply_config once again to validate the layer height profiles at all the newly added PrintObjects.
|
||||
$sprint->apply_config($config);
|
||||
|
||||
if ($opt{export_svg}) {
|
||||
$sprint->export_svg;
|
||||
if ($opt{export_png}) {
|
||||
# $sprint->export_png;
|
||||
$appctl->set_model($model);
|
||||
$appctl->set_print($sprint->_print);
|
||||
$appctl->print_ctl()->slice_to_png();
|
||||
} else {
|
||||
my $t0 = [gettimeofday];
|
||||
# The following call may die if the output_filename_format template substitution fails,
|
||||
|
@ -281,7 +282,7 @@ Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...
|
|||
and [input_filename] (default: $config->{output_filename_format})
|
||||
--post-process Generated G-code will be processed with the supplied script;
|
||||
call this more than once to process through multiple scripts.
|
||||
--export-svg Export a SVG file containing slices instead of G-code.
|
||||
--export-png Export zipped PNG files containing slices instead of G-code.
|
||||
-m, --merge If multiple files are supplied, they will be composed into a single
|
||||
print rather than processed individually.
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
{
|
||||
"path": ".",
|
||||
// "folder_exclude_patterns": [".svn", "._d", ".metadata", ".settings"],
|
||||
"file_exclude_patterns": ["XS.c"]
|
||||
"file_exclude_patterns": ["XS.c", "*.pch", "*.ilk", "*.js" ]
|
||||
}
|
||||
],
|
||||
|
||||
|
|
171
src/CMakeLists.txt
Normal file
|
@ -0,0 +1,171 @@
|
|||
project(Slic3r-native)
|
||||
|
||||
add_subdirectory(admesh)
|
||||
add_subdirectory(avrdude)
|
||||
# boost/nowide
|
||||
add_subdirectory(boost)
|
||||
add_subdirectory(clipper)
|
||||
add_subdirectory(miniz)
|
||||
add_subdirectory(polypartition)
|
||||
add_subdirectory(poly2tri)
|
||||
add_subdirectory(qhull)
|
||||
add_subdirectory(Shiny)
|
||||
add_subdirectory(semver)
|
||||
|
||||
# Adding libnest2d project for bin packing...
|
||||
set(LIBNEST2D_UNITTESTS ON CACHE BOOL "Force generating unittests for libnest2d")
|
||||
add_subdirectory(libnest2d)
|
||||
|
||||
include_directories(${LIBDIR}/qhull/src)
|
||||
#message(STATUS ${LIBDIR}/qhull/src)
|
||||
|
||||
# ##############################################################################
|
||||
# Configure rasterizer target
|
||||
# ##############################################################################
|
||||
|
||||
find_package(PNG QUIET)
|
||||
|
||||
option(RASTERIZER_FORCE_BUILTIN_LIBPNG "Force the usage of builting libpng instead of the system version." OFF)
|
||||
|
||||
if(PNG_FOUND AND NOT RASTERIZER_FORCE_BUILTIN_LIBPNG)
|
||||
message(STATUS "Using system libpng.")
|
||||
else()
|
||||
set(ZLIB_LIBRARY "")
|
||||
message(WARNING "Using builtin libpng. This can cause crashes on some platforms.")
|
||||
add_subdirectory(png/zlib)
|
||||
set(ZLIB_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/png/zlib ${CMAKE_CURRENT_BINARY_DIR}/png/zlib)
|
||||
include_directories(${ZLIB_INCLUDE_DIR})
|
||||
add_subdirectory(png/libpng)
|
||||
set_target_properties(zlibstatic PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
set_target_properties(png_static PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
set(PNG_LIBRARIES png_static zlibstatic)
|
||||
set(PNG_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR} ${PROJECT_SOURCE_DIR}/png/libpng ${CMAKE_CURRENT_BINARY_DIR}/png/libpng)
|
||||
endif()
|
||||
|
||||
add_subdirectory(libslic3r)
|
||||
|
||||
if (SLIC3R_GUI)
|
||||
message(STATUS "WXWIN environment set to: $ENV{WXWIN}")
|
||||
find_package(wxWidgets REQUIRED COMPONENTS base core adv html gl)
|
||||
include(${wxWidgets_USE_FILE})
|
||||
endif()
|
||||
|
||||
add_subdirectory(slic3r)
|
||||
|
||||
# Create a slic3r executable
|
||||
# Process mainfests for various platforms.
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/slic3r.rc.in ${CMAKE_CURRENT_BINARY_DIR}/slic3r.rc @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/msw/slic3r.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/slic3r.manifest @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platform/osx/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist @ONLY)
|
||||
if (MSVC)
|
||||
add_library(slic3r SHARED slic3r.cpp)
|
||||
else ()
|
||||
add_executable(slic3r slic3r.cpp)
|
||||
endif ()
|
||||
if (NOT MSVC)
|
||||
if(SLIC3R_GUI)
|
||||
set_target_properties(slic3r PROPERTIES OUTPUT_NAME "slic3r-gui")
|
||||
else()
|
||||
set_target_properties(slic3r PROPERTIES OUTPUT_NAME "slic3r-console")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
target_link_libraries(slic3r libslic3r)
|
||||
if (APPLE)
|
||||
# add_compile_options(-stdlib=libc++)
|
||||
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)
|
||||
# -liconv: boost links to libiconv by default
|
||||
target_link_libraries(slic3r "-liconv -framework IOKit" "-framework CoreFoundation" -lc++)
|
||||
elseif (MSVC)
|
||||
# Manifest is provided through slic3r.rc, don't generate your own.
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO")
|
||||
else ()
|
||||
target_link_libraries(slic3r -ldl -lstdc++)
|
||||
endif ()
|
||||
|
||||
# Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries.
|
||||
if (SLIC3R_GUI)
|
||||
target_link_libraries(slic3r libslic3r_gui ${wxWidgets_LIBRARIES})
|
||||
|
||||
# Configure libcurl & OpenSSL
|
||||
find_package(CURL REQUIRED)
|
||||
target_include_directories(slic3r PRIVATE ${CURL_INCLUDE_DIRS})
|
||||
target_link_libraries(slic3r CURL::libcurl)
|
||||
if (SLIC3R_STATIC)
|
||||
if (NOT APPLE)
|
||||
# libcurl is always linked dynamically to the system libcurl on OSX.
|
||||
# On other systems, libcurl is linked statically if SLIC3R_STATIC is set.
|
||||
target_compile_definitions(slic3r PRIVATE CURL_STATICLIB)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# As of now, our build system produces a statically linked libcurl,
|
||||
# which links the OpenSSL library dynamically.
|
||||
find_package(OpenSSL REQUIRED)
|
||||
message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}")
|
||||
message("OpenSSL libraries: ${OPENSSL_LIBRARIES}")
|
||||
target_include_directories(slic3r PRIVATE ${OPENSSL_INCLUDE_DIR})
|
||||
target_link_libraries(slic3r ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
target_link_libraries(slic3r user32.lib Setupapi.lib OpenGL32.Lib GlU32.Lib)
|
||||
elseif (MINGW)
|
||||
target_link_libraries(slic3r -lopengl32)
|
||||
elseif (APPLE)
|
||||
target_link_libraries(slic3r "-framework OpenGL")
|
||||
else ()
|
||||
target_link_libraries(slic3r -ldl -lGL -lGLU)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# 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.
|
||||
if (MSVC)
|
||||
add_executable(slic3r_app_gui WIN32 slic3r_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/slic3r.rc)
|
||||
target_compile_definitions(slic3r_app_gui PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE -DSLIC3R_WRAPPER_GUI)
|
||||
add_dependencies(slic3r_app_gui slic3r)
|
||||
set_target_properties(slic3r_app_gui PROPERTIES OUTPUT_NAME "slic3r")
|
||||
|
||||
add_executable(slic3r_app_console slic3r_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/slic3r.rc)
|
||||
target_compile_definitions(slic3r_app_console PRIVATE -DSLIC3R_WRAPPER_CONSOLE -DSLIC3R_WRAPPER_NOGUI)
|
||||
add_dependencies(slic3r_app_console slic3r)
|
||||
set_target_properties(slic3r_app_console PROPERTIES OUTPUT_NAME "slic3r-console")
|
||||
|
||||
add_executable(slic3r_app_noconsole WIN32 slic3r_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/slic3r.rc)
|
||||
target_compile_definitions(slic3r_app_noconsole PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE -DSLIC3R_WRAPPER_NOGUI)
|
||||
add_dependencies(slic3r_app_noconsole slic3r)
|
||||
set_target_properties(slic3r_app_noconsole PROPERTIES OUTPUT_NAME "slic3r-noconsole")
|
||||
endif ()
|
||||
|
||||
# Link the resources dir to where Slic3r GUI expects it
|
||||
if (MSVC)
|
||||
if (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}/resources" WIN_RESOURCES_SYMLINK)
|
||||
add_custom_command(TARGET slic3r POST_BUILD
|
||||
COMMAND if exist "${WIN_CONF_OUTPUT_DIR}" "("
|
||||
if not exist "${WIN_RESOURCES_SYMLINK}" "("
|
||||
mklink /J "${WIN_RESOURCES_SYMLINK}" "${SLIC3R_RESOURCES_DIR_WIN}"
|
||||
")"
|
||||
")"
|
||||
COMMENT "Symlinking the resources directory into the build tree"
|
||||
VERBATIM
|
||||
)
|
||||
endforeach ()
|
||||
else ()
|
||||
file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/resources" WIN_RESOURCES_SYMLINK)
|
||||
add_custom_command(TARGET slic3r POST_BUILD
|
||||
COMMAND if not exist "${WIN_RESOURCES_SYMLINK}" "(" mklink /J "${WIN_RESOURCES_SYMLINK}" "${SLIC3R_RESOURCES_DIR_WIN}" ")"
|
||||
COMMENT "Symlinking the resources directory into the build tree"
|
||||
VERBATIM
|
||||
)
|
||||
endif ()
|
||||
else ()
|
||||
add_custom_command(TARGET slic3r POST_BUILD
|
||||
COMMAND ln -sf "${SLIC3R_RESOURCES_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/../resources"
|
||||
COMMENT "Symlinking the resources directory into the build tree"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
25
src/Shiny/CMakeLists.txt
Normal file
|
@ -0,0 +1,25 @@
|
|||
project(Shiny)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
add_library(Shiny STATIC
|
||||
Shiny.h
|
||||
ShinyConfig.h
|
||||
ShinyData.h
|
||||
ShinyMacros.h
|
||||
ShinyManager.c
|
||||
ShinyManager.h
|
||||
ShinyNode.c
|
||||
ShinyNode.h
|
||||
ShinyNodePool.c
|
||||
ShinyNodePool.h
|
||||
ShinyNodeState.c
|
||||
ShinyNodeState.h
|
||||
ShinyOutput.c
|
||||
ShinyOutput.h
|
||||
ShinyPrereqs.h
|
||||
ShinyTools.c
|
||||
ShinyTools.h
|
||||
ShinyVersion.h
|
||||
ShinyZone.c
|
||||
ShinyZone.h
|
||||
)
|